﻿using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using EntryEngine;
using EntryEngine.Network;
using EntryEngine.Serialize;

namespace EntryBuilder
{
    partial class Program
    {
        private static MethodInfo[] Methods;

        [STAThread]
        static void Main(string[] args)
        {
            _LOG._Logger = new LoggerConsole();

            //Console.ReadKey();
            //return;

            Methods = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.Public);

            try
            {
                if (args.Length > 0)
                {
                    Invoke(args);
                }
                else
                {
                    while (true)
                    {
                        for (int i = 0; i < Methods.Length; i++)
                        {
                            MethodInfo method = Methods[i];
                            string param = "";
                            ParameterInfo[] parameters = method.GetParameters();
                            foreach (ParameterInfo parameter in parameters)
                            {
                                param += string.Format(" {0}({1})", parameter.Name, parameter.ParameterType.Name);
                            }
                            Console.WriteLine("{0}. {1} {2}", i, method.Name, param);
                        }
                        string input = Console.ReadLine();
                        args = input.Split(new char[] { ' ' }, StringSplitOptions.None);
                        if (args.Length == 0)
                            break;
                        Invoke(args);
                    }
                }
            }
            catch (Exception ex)
            {
                _LOG.Error(ex, "命令执行错误");
                Console.ReadKey();
            }
        }

        private static void Invoke(string[] args)
        {
            string symbol = args[0];
            MethodInfo method = Methods.FirstOrDefault(m => m.Name == symbol);
            if (method == null)
                method = Methods[int.Parse(symbol)];
            ParameterInfo[] parameters = method.GetParameters();

            int paramCount = parameters.Length;
            object[] param = new object[paramCount];
            for (int i = 0; i < paramCount; i++)
                param[i] = Convert.ChangeType(args[i + 1], parameters[i].ParameterType);
            if (args.Length > paramCount + 1)
                Environment.CurrentDirectory = Path.GetFullPath(args.Last());
            method.Invoke(null, param);

            Console.WriteLine("invoke {0} completed!", method.Name);
            Console.WriteLine();
        }


        private static Dictionary<string, Assembly> resolvedAssemblies;
        private static Type GetDllType(string dllAndType)
        {
            if (resolvedAssemblies == null)
            {
                resolvedAssemblies = new Dictionary<string, Assembly>();
                AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
                {
                    if (resolvedAssemblies.TryGetValue(args.Name, out Assembly ass))
                        return ass;
                    string filepath = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
                    if (File.Exists(filepath))
                    {
                        ass = Assembly.Load(File.ReadAllBytes(filepath));
                        resolvedAssemblies[args.Name] = ass;
                    }
                    Console.WriteLine("找不到程序集：{0} 是否存在：{1} 加载成功：{2}", Path.GetFullPath(filepath), File.Exists(filepath), ass != null);
                    return ass;
                };
            }

            // path\Dll.dll\Namespace.Class
            dllAndType = dllAndType.Replace('/', '\'');
            int index = dllAndType.LastIndexOf('\\');
            string lib = Path.GetFullPath(dllAndType.Substring(0, index));
            string libName = Path.GetFileNameWithoutExtension(lib);
            var name = AppDomain.CurrentDomain.GetAssemblies()[0].GetName();
            Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == libName);
            if (assembly == null)
                //assembly = Assembly.LoadFrom(lib);
                assembly = Assembly.LoadFile(lib);
            if (assembly == null)
            {
                Console.WriteLine("no library: {0}", lib);
                return null;
            }
            // Class
            string type = dllAndType.Substring(index + 1);
            Type result = assembly.GetType(type);
            if (result == null)
                Console.WriteLine("no type: {0}", type);
            return result;
        }
        private static void SaveCode(string file, StringBuilder code)
        {
            SaveCode(file, code.ToString());
        }
        private static void SaveCode(string file, string code)
        {
            File.WriteAllText(file, _RH.Indent(code), Encoding.UTF8);
        }
        private static string GetMySqlType(Type type, MemberInfo member)
        {
            bool special;
            return GetMySqlType(type, out special, member);
        }
        private static string GetMySqlType(Type type, out bool special, MemberInfo member)
        {
            special = false;
            if (type == typeof(bool))
                return "TINYINT(1)";
            else if (type == typeof(sbyte))
                return "TINYINT";
            else if (type == typeof(byte))
                return "TINYINT UNSIGNED";
            else if (type == typeof(short))
                return "SMALLINT";
            else if (type == typeof(ushort))
                return "SMALLINT UNSIGNED";
            else if (type == typeof(char))
                return "CHAR";
            else if (type == typeof(int))
                return "INT";
            else if (type == typeof(uint))
                return "INT UNSIGNED";
            else if (type == typeof(float))
                return "FLOAT";
            else if (type == typeof(long))
                return "BIGINT";
            else if (type == typeof(ulong))
                return "BIGINT UNSIGNED";
            else if (type == typeof(double))
                return "DOUBLE";
            else if (type == typeof(TimeSpan))
                return "TIME";
            else if (type == typeof(DateTime))
                return "DATETIME";
            else if (type == typeof(string))
            {
                if (member != null)
                {
                    var attr = member.GetCustomAttribute<CharAttribute>(true);
                    if (attr != null)
                        return attr.ToString();
                }
                return "VARCHAR(32)";
            }
            else if (type == typeof(byte[]))
                return "LONGBLOB";
            else if (type.IsEnum)
            {
                type = Enum.GetUnderlyingType(type);
                return GetMySqlType(type, out special, member);
            }
            else
            {
                Type nullable;
                if (type.IsNullable(out nullable))
                    return GetMySqlType(nullable, out special, member);
                special = true;
                //throw new ArgumentException("type");
                return "JSON";
            }
        }
        private static string GetTSType(Type type)
        {
            if (type == typeof(bool))
                return "Boolean";
            else if (type == typeof(sbyte)
                || type == typeof(byte)
                || type == typeof(short)
                || type == typeof(ushort)
                || type == typeof(char)
                || type == typeof(int)
                || type == typeof(uint)
                || type == typeof(float)
                || type == typeof(long)
                || type == typeof(ulong)
                || type == typeof(double)
                || type == typeof(TimeSpan))
                return "Number";
            else if (type == typeof(DateTime))
                return "Date";
            else if (type == typeof(string))
                return "String";
            else if (type.IsArray)
                return GetTSType(type.GetElementType()) + "[]";
            else if (type.IsGenericType)
            {
                var arguments = type.GetGenericArguments();
                var definition = type.GetGenericTypeDefinition();
                if (definition == typeof(Dictionary<,>))
                    return string.Format("Map<{0}, {1}>", GetTSType(arguments[0]), GetTSType(arguments[1]));
                else if (definition == typeof(List<>)
                    || definition == typeof(Queue<>)
                    || definition == typeof(Stack<>))
                    return string.Format("Array<{0}>", GetTSType(arguments[0]));
                else
                    throw new NotImplementedException("不支持的类型：" + type.FullName);
            }
            else
            {
                return type.Name;
            }
        }
        class TreeField : Tree<TreeField>
        {
            public FieldInfo Field
            {
                get;
                private set;
            }
            public Type Table
            {
                get { return Field.ReflectedType; }
            }
            public TreeField()
            {
            }
            public TreeField(FieldInfo field)
            {
                this.Field = field;
            }
        }
        class ProtocolDefault
        {
            public static Type DELEGATE_TYPE = typeof(Delegate);

            public bool IsUseJson;
            protected string Writer
            {
                get { return (IsUseJson ? typeof(ByteWriterJson) : typeof(ByteWriter)).Name; }
            }
            protected string Reader
            {
                get { return (IsUseJson ? typeof(ByteReaderJson) : typeof(ByteReader)).Name; }
            }
            public StringBuilder builder
            {
                get;
                private set;
            }
            public StringBuilder server
            {
                get;
                private set;
            }
            protected Type type
            {
                get;
                private set;
            }
            protected ProtocolStubAttribute agent
            {
                get;
                private set;
            }
            /// <summary>前端代码的后缀，例如C#就是cs，JS就是js</summary>
            protected virtual string ClientSuffix { get { return "cs"; } }
            public string OutputClientPath { get; protected set; }
            public string OutputServerPath { get; protected set; }

            public void Write(Type type, string outputClientPath, string outputServerPath)
            {
                if (type == null)
                    throw new ArgumentNullException("type");
                this.type = type;
                this.OutputClientPath = outputClientPath;
                this.OutputServerPath = outputServerPath;

                this.agent = type.GetAttribute<ProtocolStubAttribute>();
                if (agent == null)
                    throw new ArgumentNullException("agent", string.Format("{0} don't have a {1}", type.FullName, typeof(ProtocolStubAttribute).Name));

                MethodInfo[] call = type.GetInterfaceMethods().ToArray();
                MethodInfo[] callback = agent.Callback == null ? new MethodInfo[0] : agent.Callback.GetInterfaceMethods().ToArray();
                Dictionary<int, Type> asyncCB = new Dictionary<int, Type>();
                for (int i = 0; i < call.Length; i++)
                {
                    //if (call[i].ReturnType != typeof(void))
                    //    throw new NotImplementedException("Call function can't have a return value.");
                    var list = call[i].GetParameters().Where(p => p.ParameterType.Is(DELEGATE_TYPE)).ToList();
                    if (list.Count > 1)
                        throw new NotSupportedException("Delegate parameter can have only one.");
                    ParameterInfo d = list.FirstOrDefault();
                    if (d != null)
                        asyncCB.Add(i, d.ParameterType);
                }

                if (builder == null)
                    builder = new StringBuilder();
                if (server == null)
                    server = new StringBuilder();
                Write(call, callback, asyncCB);
            }
            protected virtual void Write(MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                WNamespace(builder);
                WNamespace(server);

                WSInterface(server, call, asyncCB);
                WSCallbackType(server, call, asyncCB);
                WSCallbackProxy(server, call, callback, asyncCB);
                WSAgentStub(server, call, asyncCB);

                WCCallProxy(builder, call, callback, asyncCB);
                // 生成默认的代理人
                if (agent.Callback != null)
                    WCDefaultAgentStub(builder, callback);
            }
            protected virtual void WNamespace(StringBuilder builder)
            {
                builder.AppendLine("using System;");
                builder.AppendLine("using System.Collections.Generic;");
                builder.AppendLine("using EntryEngine;");
                builder.AppendLine("using EntryEngine.Network;");
                builder.AppendLine("using EntryEngine.Serialize;");
                if (!string.IsNullOrEmpty(type.Namespace))
                    builder.AppendLine("using {0};", type.Namespace);
                builder.AppendLine();
            }
            protected virtual void WSInterface(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                bool hasAgent = !string.IsNullOrEmpty(agent.AgentType);

                builder.AppendLine("interface _{0}", type.Name);
                builder.AppendBlock(() =>
                {
                    for (int i = 0, n = call.Length; i < n; i++)
                    {
                        MethodInfo method = call[i];
                        builder.Append("void {0}(", method.Name);
                        bool hasDelegate = asyncCB.ContainsKey(i);
                        bool notFirst = false;
                        // 有指代理类型时，第一个参数为代理类型参数
                        if (hasAgent)
                        {
                            builder.Append("{0} __client", agent.AgentType);
                            notFirst = true;
                        }
                        var parameters = method.GetParameters();
                        foreach (ParameterInfo param in parameters)
                        {
                            // 不是第一个参数需要逗号分隔
                            if (notFirst)
                                builder.Append(", ");
                            // 参数类型，委托类型参数是生成的类型
                            if (hasDelegate && param.ParameterType.Is(typeof(Delegate)))
                                builder.Append("CB{0}_{1}", type.Name, method.Name);
                            else
                                builder.Append(param.ParameterType.CodeName());
                            // 参数名
                            builder.Append(" {0}", param.Name);
                            notFirst = true;
                        }
                        builder.AppendLine(");");
                    }
                });
                builder.AppendLine();
            }
            protected virtual void WSCallbackType(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                foreach (var item in asyncCB)
                {
                    string name = string.Format("CB{0}_{1}", type.Name, call[item.Key].Name);
                    builder.AppendLine("class {0} : IDisposable", name);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("private byte __id;");
                        builder.AppendLine("private Link __link;");
                        //builder.AppendLine("\tinternal int? Delay;");
                        builder.AppendLine("internal bool IsCallback { get; private set; }");
                        builder.AppendLine("public {0}(byte id, Link link)", name);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("this.__id = id;");
                            builder.AppendLine("this.__link = link;");
                        });

                        // 回调方法头
                        builder.Append("\tpublic void Callback(");
                        var parameters = item.Value.GetMethod("Invoke").GetParameters();
                        for (int i = 0, n = parameters.Length; i < n; i++)
                        {
                            if (i > 0)
                                builder.Append(", ");
                            var param = parameters[i];
                            builder.AppendFormat("{0} {1}", param.ParameterType.CodeName(), param.Name);
                        }
                        builder.AppendLine(") // INDEX = {0}", item.Key);
                        // 具体实现
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (IsCallback) return;");
                            // 参数
                            builder.AppendLine("{0} __writer = new {0}();", Writer);
                            builder.AppendLine("__writer.Write(__id);");
                            builder.AppendLine("__writer.Write((sbyte)0);");
                            foreach (ParameterInfo param in parameters)
                                builder.AppendLine("__writer.Write({0});", param.Name);
                            // 记录日志
                            builder.AppendLine("#if DEBUG");
                            builder.Append("_LOG.Debug(\"{0} Callback({{0}} bytes)", name);
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                ParameterInfo param = parameters[j];
                                builder.Append(" {0}: {{{1}}}", param.Name, j + 1);
                                if (j != n)
                                    builder.Append(",");
                            }
                            builder.Append("\", __writer.Position");
                            foreach (ParameterInfo param in parameters)
                                builder.Append(", JsonWriter.Serialize({0})", param.Name);
                            builder.AppendLine(");");
                            builder.AppendLine("#endif");
                            // 回调
                            builder.AppendLine("{0}Proxy.{1}_{2}(__link, __writer.Buffer, __writer.Position);", agent.Callback == null ? type.Name + "Callback" : agent.Callback.Name, call[item.Key].Name, item.Key);
                            builder.AppendLine("IsCallback = true;");
                        });

                        // 请求错误
                        builder.AppendLine("public void Error(sbyte ret, string msg)");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (IsCallback) return;");
                            builder.AppendLine("{0} __writer = new {0}();", Writer);
                            // 协议及参数信息
                            builder.AppendLine("__writer.Write((byte){0});", agent.Protocol);
                            builder.AppendLine("__writer.Write((ushort){0});", item.Key);
                            builder.AppendLine("__writer.Write(__id);");
                            builder.AppendLine("__writer.Write(ret);");
                            builder.AppendLine("__writer.Write(msg);");
                            // 日志
                            builder.AppendLine("#if DEBUG");
                            builder.AppendLine("_LOG.Debug(\"{0} Error({{0}} bytes) ret={{1}} msg={{2}}\", __writer.Position, ret, msg);", name);
                            builder.AppendLine("#endif");
                            // 回调
                            builder.AppendLine("__link.Write(__writer.Buffer, 0, __writer.Position);");
                            builder.AppendLine("IsCallback = true;");
                        });

                        // async callback
                        // 接口的处理方法涉及异步（例如登录，需要查询数据库）
                        // 不能立刻进行回调
                        // 需要延迟回调，当延迟时间过后仍未回调
                        // 再自动调用callback.Error
                        //builder.AppendLine("\tpublic void DelayCallback(int ms)");
                        //builder.AppendLine("\t{");
                        //builder.AppendLine("\t\tif (IsCallback) return;");
                        //builder.AppendLine("\t\tDelay = ms;");
                        //builder.AppendLine("\t}");
                        builder.AppendLine("public void Dispose()");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (!IsCallback) Error(-2, \"no callback\");");
                        });
                    });
                }
                builder.AppendLine();
            }
            protected virtual void WSCallbackProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                if (agent.Callback == null)
                    builder.AppendLine("static class {0}CallbackProxy", type.Name);
                else
                    builder.AppendLine("static class {0}Proxy", agent.Callback.Name);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("public static Action<ByteWriter> __WriteAgent;");
                    for (int i = 0, offset = 0,
                        len = callback.Length, n = call.Length + len;
                        i < n; i++)
                    {
                        // 调用中含有委托参数的方法和回调接口都需要生成回调函数
                        bool isDelegate = asyncCB.ContainsKey(i);
                        MethodInfo method;
                        if (isDelegate)
                        {
                            method = call[i];
                            offset++;
                        }
                        else
                        {
                            if (i - offset >= len)
                                continue;
                            method = callback[i - offset];
                        }

                        // 方法头
                        ParameterInfo[] parameters = null;
                        if (isDelegate)
                            builder.AppendLine("internal static void {0}_{1}(Link __link, byte[] data, int position)", method.Name, i);
                        else
                        {
                            builder.Append("public static {0} {1}(Link __link", method.ReturnType.CodeName(), method.Name);
                            parameters = method.GetParameters();
                            foreach (ParameterInfo param in parameters)
                                builder.AppendFormat(", {0} {1}", param.ParameterType.CodeName(), param.Name);
                            builder.AppendLine(")");
                        }

                        // 方法体
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (__link == null || !__link.IsConnected) return;");
                            builder.AppendLine("{0} __writer = new {0}();", Writer);
                            builder.AppendLine("if (__WriteAgent != null) __WriteAgent(__writer);");
                            // 协议
                            builder.AppendLine("__writer.Write((byte){0});", agent.Protocol);
                            //builder.AppendLine("__writer.Write((ushort){0});", i);
                            builder.AppendLine("__writer.Write(\"{0}\");", method.Name);
                            // 回调参数内容
                            if (isDelegate)
                            {
                                builder.AppendLine("__writer.WriteBytes(data, 0, position);", i);
                                //builder.AppendLine("#if !OUTPUT");
                                //builder.AppendFormatLine("\t\tLog.Debug(\"async {0}.{1}({{0}} bytes)\", writer.Position);", type.Name, method.Name);
                                //builder.AppendLine("#endif");
                            }
                            else
                            {
                                foreach (ParameterInfo param in parameters)
                                    builder.AppendLine("__writer.Write({0});", param.Name);
                                // 日志
                                builder.AppendLine("#if DEBUG");
                                builder.AppendFormat("_LOG.Debug(\"{0}({{0}} bytes)", method.Name);
                                for (int j = 0, m = parameters.Length - 1; j <= m; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    builder.Append(" {0}: {{{1}}}", param.Name, j + 1);
                                    if (j != m)
                                        builder.Append(",");
                                }
                                builder.Append("\", __writer.Position");
                                for (int j = 0, m = parameters.Length - 1; j <= m; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    if (!param.ParameterType.IsCustomType())
                                        builder.AppendFormat(", {0}", param.Name);
                                    else
                                        builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                                }
                                builder.AppendLine(");");
                                builder.AppendLine("#endif");
                            }
                            builder.AppendLine("__link.Write(__writer.Buffer, 0, __writer.Position);");
                        });
                    }
                });
                builder.AppendLine();
            }
            protected virtual void WSAgentStub(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                bool hasAgent = !string.IsNullOrEmpty(agent.AgentType);

                string name = type.Name + "Stub";
                builder.AppendLine("class {0} : {1}", name, typeof(Stub).Name);
                builder.AppendBlock(() =>
                {
                    string name2 = hasAgent ? agent.AgentType : "_" + type.Name;

                    builder.AppendLine("public _{0} __Agent;", type.Name);
                    builder.AppendLine("public Func<_{0}> __GetAgent;", type.Name);
                    builder.AppendLine("public Func<ByteReader, {0}> __ReadAgent;", name2);
                    builder.AppendLine("public {0}(_{1} agent) : base({2})", name, type.Name, agent.Protocol);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__Agent = agent;");
                        foreach (var method in call)
                            builder.AppendLine("AddMethod(\"{0}\", {0});", method.Name);
                    });
                    builder.AppendLine("public {0}(Func<_{1}> agent) : this((_{1})null)", name, type.Name, agent.Protocol);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__GetAgent = agent;");
                    });
                    builder.AppendLine("public {0}(Func<ByteReader, {1}> agent) : this((_{2})null)", name, name2, type.Name);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__ReadAgent = agent;");
                    });
                    // 根据方法参数生成对应的处理方法存根
                    for (int i = 0; i < call.Length; i++)
                    {
                        MethodInfo method = call[i];
                        bool isDelegate = asyncCB.ContainsKey(i);
                        // 方法头
                        builder.AppendLine("{0} {1}(ByteReader __stream)", method.ReturnType.CodeName(), method.Name);

                        ParameterInfo[] parameters = method.GetParameters();
                        builder.AppendBlock(() =>
                        {
                            if (IsUseJson)
                            {
                                builder.AppendLine("var __temp = __stream;");
                                builder.AppendLine("__stream = new ByteReaderJson(__stream.Buffer, __stream.Position);");
                            }
                            //builder.AppendLine("Timer timer = Timer.StartNew();");    // 统计方法调用次数，可用于分析用户操作
                            builder.AppendLine("var agent = __Agent;");
                            builder.AppendLine("if (__GetAgent != null) { var temp = __GetAgent(); if (temp != null) agent = temp; }");
                            if (hasAgent)
                            {
                                // 指定了代理人类型时，处理方法首个参数将可由读取委托来获得
                                builder.AppendLine("var __client = default({0});", agent.AgentType);
                                builder.AppendLine("if (__ReadAgent != null) { var temp = __ReadAgent(__stream);if (temp != null) __client = temp; }");
                            }
                            else
                            {
                                // 没有指定代理人类型时，将采用默认代理人
                                builder.AppendLine("if (__ReadAgent != null) { var temp = __ReadAgent(__stream); if (temp != null) agent = temp; }");
                            }
                            // 参数声明
                            foreach (ParameterInfo param in parameters)
                            {
                                if (isDelegate && param.ParameterType.Is(typeof(Delegate)))
                                {
                                    builder.AppendLine("byte {0};", param.Name);
                                    name = param.Name;
                                }
                                else
                                    builder.AppendLine("{0} {1};", param.ParameterType.CodeName(), param.Name);
                            }
                            // 参数读取赋值
                            foreach (ParameterInfo param in parameters)
                                builder.AppendLine("__stream.Read(out {0});", param.Name);
                            // 若修改了读取器，也要让原本的读取器一起读取数据，以让底层的读取结束
                            if (IsUseJson)
                            {
                                builder.AppendLine("__temp.Seek(__stream.Position);");
                            }
                            // 日志
                            builder.AppendLine("#if DEBUG");
                            builder.AppendFormat("_LOG.Debug(\"{0}", method.Name);
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                ParameterInfo param = parameters[j];
                                builder.AppendFormat(" {0}: {{{1}}}", param.Name, j);
                                if (j != n)
                                    builder.Append(",");
                            }
                            builder.Append("\"");
                            for (int j = 0; j < parameters.Length; j++)
                            {
                                ParameterInfo param = parameters[j];
                                if (param.ParameterType.Is(typeof(Delegate)))
                                    builder.AppendFormat(", \"{0}\"", param.ParameterType.CodeName());
                                else if (!param.ParameterType.IsCustomType())
                                    builder.AppendFormat(", {0}", param.Name);
                                else
                                    builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                            }
                            builder.AppendLine(");");
                            builder.AppendLine("#endif");
                            // 让代理人根据参数调用代理函数
                            if (isDelegate)
                            {
                                const string CB = "__callback";
                                builder.AppendLine("var {1} = new CB{2}_{3}({0}, Link);", name, CB, type.Name, method.Name);
                                builder.AppendLine("try");
                                builder.AppendBlock(() =>
                                {
                                    builder.AppendFormat("agent.{0}({1}", method.Name, hasAgent ? "__client, " : "");
                                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                    {
                                        ParameterInfo param = parameters[j];
                                        if (j == n)
                                            builder.AppendFormat(CB);
                                        else
                                        {
                                            builder.AppendFormat("{0}", param.Name);
                                            builder.Append(", ");
                                        }
                                    }
                                    builder.AppendLine(");");
                                });
                                builder.AppendLine("catch (Exception ex)");
                                builder.AppendBlock(() =>
                                {
                                    builder.AppendLine("_LOG.Error(\"Callback_{0} error! msg={{0}} stack={{1}}\", ex.Message, ex.StackTrace);", method.Name);
                                    builder.AppendLine("if (!{0}.IsCallback) {0}.Error(-1, ex.Message);", CB);
                                });
                                // 没有回调不管，可能是进行了异步操作，等待其IDisposable.Dispose调用自然回调no callback消息
                                //builder.AppendLine("finally");
                                //builder.AppendBlock(() =>
                                //{
                                //    // 1. if (!__callback.IsCallback) __callback.Error(-2, "no callback");
                                //    // 2. if (__callback.Delay.HasValue) EntryService.Instance.Delay(__callback.Delay.Value, () => { if (!__callback.IsCallback) __callback.Error(-2, "no callback"); });
                                //    builder.AppendLine("if (!{0}.IsCallback)", CB);
                                //    builder.AppendLine("if ({0}.Delay.HasValue && {0}.Delay.Value > 0) EntryService.Instance.Delay(__callback.Delay.Value, () => {{ if (!{0}.IsCallback) {0}.Error(-2, \"no callback\"); }});", CB);
                                //    builder.AppendLine("else {0}.Error(-2, \"no callback\");", CB);
                                //});
                            }
                            else
                            {
                                builder.AppendFormat("agent.{0}({1}", method.Name, hasAgent ? (parameters.Length == 0 ? "__client" : "__client, ") : "");
                                for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    builder.Append("{0}", param.Name);
                                    if (j != n)
                                        builder.Append(", ");
                                }
                                builder.AppendLine(");");
                            }
                            //builder.AppendLine("\t\tStatistic.Add(\"{0}.{1}\", timer.Stop());", type.Name, method.Name);
                        });
                    }
                });
            }
            protected virtual void WCCallProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                string name = type.Name + "Proxy";
                string cbname = agent.Callback != null ? agent.Callback.CodeName() : null;

                if (asyncCB.Count == 0)
                    builder.AppendLine("class {0} : {1}, {2}", name, typeof(StubClientAsync).Name, type.Name);
                else
                    builder.AppendLine("class {0} : {1}", name, typeof(StubClientAsync).Name);
                builder.AppendBlock(() =>
                {
                    if (agent.Callback != null)
                    {
                        builder.AppendLine("public {0} __Agent;", cbname);
                        builder.AppendLine("public Func<{0}> __GetAgent;", cbname);
                        builder.AppendLine("public Func<ByteReader, {0}> __ReadAgent;", cbname);
                    }
                    builder.AppendLine("public Action<ByteWriter> __WriteAgent;");
                    builder.AppendLine();

                    // 构造函数，添加服务器回调处理方法存根
                    builder.AppendLine("public {0}()", name);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.Protocol = {0};", agent.Protocol);
                        // add method
                        for (int i = 0, offset = 0,
                             len = callback.Length, n = call.Length + len;
                             i < n; i++)
                        {
                            bool isDelegate = asyncCB.ContainsKey(i);
                            MethodInfo method;
                            if (isDelegate)
                            {
                                method = call[i];
                                offset++;
                            }
                            else
                            {
                                if (i - offset >= len)
                                    continue;
                                method = callback[i - offset];
                            }
                            builder.AppendLine("AddMethod(\"{0}\", {0}_{1});", method.Name, i);
                        }
                    });
                    if (agent.Callback != null)
                    {
                        builder.AppendLine("public {0}({1} agent) : this()", name, cbname);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("this.__Agent = agent;");
                        });
                        builder.AppendLine("public {0}(Func<{1}> agent) : this()", name, cbname);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("this.__GetAgent = agent;");
                        });
                        builder.AppendLine("public {0}(Func<ByteReader, {1}> agent) : this()", name, cbname);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("this.__ReadAgent = agent;");
                        });
                    }
                    builder.AppendLine();

                    // 通过代理调用接口方法
                    WCCallProxy(builder, call, asyncCB);

                    // 通过代理人处理回调方法
                    WCAgentStub(builder, call, callback, asyncCB);
                });
            }
            protected virtual void WCCallProxy(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                for (int i = 0; i < call.Length; i++)
                {
                    MethodInfo method = call[i];
                    ParameterInfo[] parameters = method.GetParameters();

                    // 方法头
                    //bool hasAsync = parameters.Contains(p => p.ParameterType.Is(typeof(Delegate)));
                    bool hasAsync = asyncCB.ContainsKey(i);
                    if (hasAsync)
                        builder.Append("public StubClientAsync.AsyncWaitCallback {0}(", method.Name);
                    else
                        builder.Append("public void {0}(", method.Name);
                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                    {
                        if (j != 0)
                            builder.Append(", ");
                        var param = parameters[j];
                        builder.Append("{0} {1}", param.ParameterType.CodeName(), param.Name);
                    }
                    builder.AppendLine(")");
                    builder.AppendBlock(() =>
                    {
                        if (hasAsync)
                            builder.AppendLine("if (Link == null || !Link.IsConnected) return null;");
                        else
                            builder.AppendLine("if (Link == null || !Link.IsConnected) return;");
                        builder.AppendLine("{0} __writer = new {0}();", Writer);
                        builder.AppendLine("if (__WriteAgent != null) __WriteAgent(__writer);");
                        builder.AppendLine("__writer.Write((byte){0});", agent.Protocol);
                        //builder.AppendLine("__writer.Write((ushort){0});", i);
                        builder.AppendLine("__writer.Write(\"{0}\");", method.Name);
                        foreach (ParameterInfo param in parameters)
                        {
                            if (hasAsync && param.ParameterType.Is(typeof(Delegate)))
                            {
                                builder.AppendLine("var __async = Push({0});", param.Name);
                                builder.AppendLine("if (__async == null) return null;");
                                builder.AppendLine("__writer.Write(__async.ID);", param.Name);
                            }
                            else
                            {
                                builder.AppendLine("__writer.Write({0});", param.Name);
                            }
                        }
                        builder.AppendLine("#if DEBUG");
                        builder.AppendFormat("_LOG.Debug(\"{0}({{0}} bytes)", method.Name);
                        for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                        {
                            ParameterInfo param = parameters[j];
                            builder.AppendFormat(" {0}: {{{1}}}", param.Name, j + 1);
                            if (j != n)
                                builder.Append(",");
                        }
                        builder.Append("\", __writer.Position");
                        for (int j = 0; j < parameters.Length; j++)
                        {
                            ParameterInfo param = parameters[j];
                            if (hasAsync && param.ParameterType.Is(typeof(Delegate)))
                                builder.Append(", \"{0}\"", param.ParameterType.CodeName());
                            else if (!param.ParameterType.IsCustomType())
                                builder.Append(", {0}", param.Name);
                            else
                                builder.Append(", JsonWriter.Serialize({0})", param.Name);
                        }
                        builder.AppendLine(");");
                        builder.AppendLine("#endif");
                        builder.AppendLine("Link.Write(__writer.Buffer, 0, __writer.Position);");

                        if (hasAsync)
                            builder.AppendLine("return __async;");
                    });
                }
                builder.AppendLine();
            }
            protected virtual void WCAgentStub(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                for (int i = 0, offset = 0,
                     len = callback.Length, t = call.Length + len;
                     i < t; i++)
                {
                    bool idDelegate = asyncCB.ContainsKey(i);
                    MethodInfo method;
                    if (idDelegate)
                    {
                        method = call[i];
                        offset++;
                    }
                    else
                    {
                        if (i - offset >= len)
                            continue;
                        method = callback[i - offset];
                    }
                    builder.AppendLine("void {0}_{1}(ByteReader __stream)", method.Name, i);
                    builder.AppendBlock(() =>
                    {
                        if (IsUseJson)
                        {
                            builder.AppendLine("var __temp = __stream;");
                            builder.AppendLine("__stream = new ByteReaderJson(__stream.Buffer, __stream.Position);");
                        }
                        ParameterInfo[] parameters = method.GetParameters();
                        //builder.AppendLine("Timer timer = Timer.StartNew();");    // 统计方法调用次数，可用于分析用户操作
                        if (!idDelegate)
                        {
                            builder.AppendLine("var __callback = __Agent;");
                            builder.AppendLine("if (__GetAgent != null) { var temp = __GetAgent(); if (temp != null) __callback = temp; }");
                            builder.AppendLine("if (__ReadAgent != null) { var temp = __ReadAgent(__stream); if (temp != null) __callback = temp; }");
                        }

                        if (idDelegate)
                        {
                            builder.AppendLine("byte __id;");
                            builder.AppendLine("sbyte __ret;");
                            builder.AppendLine("__stream.Read(out __id);");
                            builder.AppendLine("__stream.Read(out __ret);");
                            builder.AppendLine("var __callback = Pop(__id);");
                            //builder.AppendLine("if (__callback == null)");
                            //builder.AppendLine("throw new InvalidOperationException(\"no async request id=\" + id);");
                            // 请求正确回调
                            builder.AppendLine("if (__ret == 0)");
                            builder.AppendBlock(() =>
                            {
                                parameters = asyncCB[i].GetMethod("Invoke").GetParameters();
                                // 参数声明
                                foreach (ParameterInfo param in parameters)
                                    builder.AppendLine("{0} {1};", param.ParameterType.CodeName(), param.Name);
                                // 参数读取赋值
                                foreach (ParameterInfo param in parameters)
                                    builder.AppendLine("__stream.Read(out {0});", param.Name);
                                // 日志
                                builder.AppendLine("#if DEBUG");
                                builder.AppendFormat("_LOG.Debug(\"{0}", method.Name);
                                for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    builder.AppendFormat(" {0}: {{{1}}}", param.Name, j);
                                    if (j != n)
                                        builder.Append(",");
                                }
                                builder.Append("\"");
                                for (int j = 0; j < parameters.Length; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                                }
                                builder.AppendLine(");");
                                builder.AppendLine("#endif");
                                // 调用回调方法
                                builder.AppendLine("var __invoke = ({0})__callback.Function;", asyncCB[i].CodeName());
                                builder.AppendFormat("if (__invoke != null) __invoke(", method.Name);
                                for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    builder.AppendFormat("{0}", param.Name);
                                    if (j != n)
                                        builder.Append(", ");
                                }
                                builder.AppendLine(");");
                            });
                            // 请求出错回调的错误信息
                            builder.AppendLine("else");
                            builder.AppendBlock(() =>
                            {
                                builder.AppendLine("string __msg;");
                                builder.AppendLine("__stream.Read(out __msg);");
                                builder.AppendLine("_LOG.Error(\"{0}_{1} error! id={{0}} ret={{1}} msg={{2}}\", __id, __ret, __msg);", method.Name, i);
                                //builder.AppendLine("Error(__callback, __id, __ret, __msg);");
                                builder.AppendLine("Error(__callback, {0}, __ret, __msg);", i);
                            });
                        }
                        else
                        {
                            // 参数声明
                            foreach (ParameterInfo param in parameters)
                                //if (!idDelegate || !param.ParameterType.Is(typeof(Delegate)))
                                builder.AppendLine("{0} {1};", param.ParameterType.CodeName(), param.Name);
                            // 参数读取赋值
                            foreach (ParameterInfo param in parameters)
                                //if (!idDelegate || !param.ParameterType.Is(typeof(Delegate)))
                                builder.AppendLine("__stream.Read(out {0});", param.Name);
                            // 日志
                            builder.AppendLine("#if DEBUG");
                            builder.AppendFormat("_LOG.Debug(\"{0}", method.Name);
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                ParameterInfo param = parameters[j];
                                builder.AppendFormat(" {0}: {{{1}}}", param.Name, j);
                                if (j != n)
                                    builder.Append(",");
                            }
                            builder.Append("\"");
                            foreach (ParameterInfo param in parameters)
                                builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                            builder.AppendLine(");");
                            builder.AppendLine("#endif");
                            // 调用回调方法
                            builder.AppendFormat("__callback.{0}(", method.Name);
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                ParameterInfo param = parameters[j];
                                builder.AppendFormat("{0}", param.Name);
                                if (j != n)
                                    builder.Append(", ");
                            }
                            builder.AppendLine(");");
                        }
                        if (IsUseJson)
                        {
                            builder.AppendLine("__temp.Seek(__stream.Position);");
                        }
                        //builder.AppendLine("\t\tStatistic.Add(\"{0}.{1}\", timer.Stop());", flag ? type.Name : agent.Callback.Name, method.Name);
                    });
                }
            }
            protected virtual void WCDefaultAgentStub(StringBuilder builder, MethodInfo[] callback)
            {
                string cbName = agent.Callback.CodeName();
                builder.AppendLine();
                builder.AppendLine("class {0}Agent : {1}", type.Name, cbName);
                builder.AppendBlock(() =>
                {
                    // build method static class
                    foreach (var method in callback)
                    {
                        builder.AppendLine("public static class _{0}", method.Name);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("public static Action GlobalCallback;");
                            builder.AppendLine("public static Action Callback;");
                            var parameters = method.GetParameters();
                            foreach (var item in parameters)
                                builder.AppendLine("public static {0} {1};", item.ParameterType.CodeName(), item.Name);
                        });
                    }
                    builder.AppendLine();

                    // build implement
                    foreach (var method in callback)
                    {
                        builder.Append("void {0}.{1}", cbName, method.Name);
                        builder.AppendMethodParametersWithBracket(method);
                        builder.AppendBlock(() =>
                        {
                            var parameters = method.GetParameters();
                            foreach (var item in parameters)
                                builder.AppendLine("_{0}.{1} = {1};", method.Name, item.Name);
                            builder.AppendMethodInvoke(method, null, "__" + method.Name, null, null);
                            builder.AppendLine("if (_{0}.GlobalCallback != null) _{0}.GlobalCallback();", method.Name);
                            builder.AppendLine("if (_{0}.Callback != null) _{0}.Callback();", method.Name);
                        });
                    }
                    foreach (var method in callback)
                    {
                        builder.Append("protected virtual void __{0}(", method.Name);
                        builder.AppendMethodParametersOnly(method);
                        builder.AppendLine("){ }");
                    }

                    // reset all local event
                    builder.AppendLine("public static void ResetAllCallback()");
                    builder.AppendBlock(() =>
                    {
                        foreach (var method in callback)
                        {
                            builder.AppendLine("_{0}.Callback = null;", method.Name);
                        }
                    });
                });
            }

            public virtual void Save()
            {
                if (!string.IsNullOrEmpty(OutputClientPath))
                {
                    string clientFile = Path.Combine(OutputClientPath, type.Name + "Proxy." + ClientSuffix);
                    SaveCode(clientFile, builder);
                }
                if (!string.IsNullOrEmpty(OutputServerPath))
                {
                    string serverFile = Path.Combine(OutputServerPath, type.Name + "Stub.cs");
                    SaveCode(serverFile, server);
                }
                Console.WriteLine("生成协议类型{0}完成", type.FullName);
                builder = null;
                server = null;
            }
            public virtual void Over() { }
        }
        class ProtocolHttp : ProtocolDefault
        {
            protected override void WNamespace(StringBuilder builder)
            {
                base.WNamespace(builder);
                builder.AppendLine("using System.Linq;");
                builder.AppendLine("using System.Text;");
                builder.AppendLine("using System.Net;");
            }
            protected override void WSInterface(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                bool hasAgent = !string.IsNullOrEmpty(agent.AgentType);

                builder.AppendLine("interface _{0}", type.Name);
                builder.AppendBlock(() =>
                {
                    foreach (var item in call)
                    {
                        bool output = item.HasReturnType();
                        if (output)
                        {
                            // 有返回类型则在Stub里自动回调
                            builder.Append("{0} {1}(", item.ReturnType.CodeName(), item.Name);
                            builder.AppendMethodParametersOnly(item);
                            builder.AppendLine(");");
                        }
                        else
                        {
                            // 没有返回类型追加HttpListenerContext参数手动回调
                            var parameters = item.GetParameters();
                            ParameterInfo delegateParameter = null;
                            bool hasDelegate = parameters.Any(p =>
                            {
                                if (p.ParameterType.Is(DELEGATE_TYPE))
                                {
                                    delegateParameter = p;
                                    return true;
                                }
                                else
                                {
                                    return false;
                                }
                            });
                            if (hasDelegate)
                            {
                                builder.Append("void {0}(", item.Name);
                                for (int i = 0, n = parameters.Length - 1; i <= n; i++)
                                {
                                    var parameter = parameters[i];
                                    if (parameter == delegateParameter)
                                    {
                                        builder.Append("CB{0}_{1} callback", type.Name, item.Name);
                                    }
                                    else
                                    {
                                        builder.Append("{0} {1}", parameter.ParameterType.CodeName(), parameter.Name);
                                    }

                                    if (i != n)
                                        builder.Append(", ");
                                }
                                builder.AppendLine(");");
                            }
                            else
                            {
                                if (item.GetParameters().Length > 0)
                                {
                                    builder.Append("void {0}(HttpListenerContext __context, ", item.Name);
                                    builder.AppendMethodParametersOnly(item);
                                    builder.AppendLine(");");
                                }
                                else
                                    builder.AppendLine("void {0}(HttpListenerContext __context);", item.Name);
                            }
                        }
                    }
                });
            }
            protected override void WSCallbackType(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                foreach (var item in asyncCB)
                {
                    string name = string.Format("CB{0}_{1}", type.Name, call[item.Key].Name);
                    builder.AppendLine("public class {0} : IDisposable", name);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("internal HttpListenerContext __context { get; private set; }");
                        builder.AppendLine("internal StubHttp __link { get; private set; }");
                        //builder.AppendLine("\tinternal int? Delay;");
                        builder.AppendLine("internal bool IsCallback { get; private set; }");
                        builder.AppendLine("public {0}(StubHttp link)", name);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("this.__link = link;");
                            builder.AppendLine("this.__context = link.Context;");
                        });

                        // 回调方法头
                        builder.Append("\tpublic void Callback(");
                        var parameters = item.Value.GetMethod("Invoke").GetParameters();
                        if (parameters.Length > 1)
                        {
                            throw new ArgumentException("委托参数不能大于1个");
                        }
                        var parameter = parameters[0];
                        builder.AppendFormat("{0} {1}", parameter.ParameterType.CodeName(), parameter.Name);
                        builder.AppendLine(") // INDEX = {0}", item.Key);
                        // 具体实现
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (IsCallback) return;");
                            builder.AppendLine("string __ret = JsonWriter.Serialize({0});", parameter.Name);
                            //builder.AppendLine("string __ret = JsonWriter.Serialize({0}, typeof({1}));", parameter.Name, parameter.ParameterType.CodeName());
                            // 记录日志
                            builder.AppendLine("#if DEBUG");
                            builder.AppendLine("_LOG.Debug(\"{0} {{0}}\", __ret);", name);
                            builder.AppendLine("#endif");
                            // 回调
                            builder.AppendLine("__link.Response(__context, __ret);");
                            builder.AppendLine("IsCallback = true;");
                        });

                        // 请求错误
                        builder.AppendLine("public void Error(int ret, string msg)");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (IsCallback) return;");
                            // 协议及参数信息
                            builder.AppendLine("string __ret = JsonWriter.Serialize(new HttpError(ret, msg));");
                            // 日志
                            builder.AppendLine("#if DEBUG");
                            builder.AppendLine("_LOG.Debug(\"{0} Error ret={{0}} msg={{1}}\", ret, msg);", name);
                            builder.AppendLine("#endif");
                            // 回调
                            builder.AppendLine("__link.Response(__context, __ret);");
                            builder.AppendLine("IsCallback = true;");
                        });

                        // async callback
                        // 接口的处理方法涉及异步（例如登录，需要查询数据库）
                        // 不能立刻进行回调
                        // 需要延迟回调，当延迟时间过后仍未回调
                        // 再自动调用callback.Error
                        //builder.AppendLine("\tpublic void DelayCallback(int ms)");
                        //builder.AppendLine("\t{");
                        //builder.AppendLine("\t\tif (IsCallback) return;");
                        //builder.AppendLine("\t\tDelay = ms;");
                        //builder.AppendLine("\t}");
                        builder.AppendLine("public void Dispose()");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (!IsCallback) Error(-2, \"no callback\");");
                        });
                    });
                }
                builder.AppendLine();
            }
            protected override void WSCallbackProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                if (agent.Callback != null)
                    _LOG.Debug("HTTP暂不支持生成主动回调的Proxy");
            }
            protected override void WSAgentStub(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                bool hasAgent = !string.IsNullOrEmpty(agent.AgentType);
                if (hasAgent)
                    _LOG.Debug("HTTP暂不支持AgentType");

                builder.AppendLine("class {0}Stub : {1}", type.Name, typeof(StubHttp).Name);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("public Action<HttpListenerContext, object> __AutoCallback;");
                    builder.AppendLine("public _{0} __Agent;", type.Name);
                    builder.AppendLine("\tpublic Func<_{0}> __GetAgent;", type.Name);
                    builder.AppendLine("public Func<HttpListenerContext, _{0}> __ReadAgent;", type.Name);
                    builder.AppendLine("public {0}Stub(_{0} agent)", type.Name);
                    if (agent != null && !string.IsNullOrEmpty(agent.AgentType))
                        builder.AppendLine(" : base(\"{0}\")", agent.AgentType);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__Agent = agent;");
                        builder.AppendLine("this.Protocol = \"{0}\";", agent.Protocol);
                        foreach (var method in call)
                            builder.AppendLine("AddMethod(\"{0}\", {0});", method.Name);
                    });

                    for (int i = 0; i < call.Length; i++)
                    //foreach (var method in call)
                    {
                        var method = call[i];
                        builder.AppendLine("void {0}(HttpListenerContext __context)", method.Name);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("var agent = __Agent;");
                            builder.AppendLine("if (__GetAgent != null) { var temp = __GetAgent(); if (temp != null) agent = temp; }");
                            builder.AppendLine("if (__ReadAgent != null) { var temp = __ReadAgent(__context); if (temp != null) agent = temp; }");

                            // 参数
                            var parameters = method.GetParameters();
                            if (parameters.Length > 0)
                            {
                                // 解析参数
                                builder.AppendLine("string __temp;");
                                //builder.AppendLine("var __query = __context.Request.QueryString;");
                                foreach (var param in parameters)
                                {
                                    if (param.ParameterType.Is(typeof(Delegate)))
                                        continue;
                                    string typeCodeName = param.ParameterType.CodeName();
                                    if (param.ParameterType == typeof(FileUpload))
                                    {
                                        builder.AppendLine("{0} {1} = GetFile(\"{1}\");", typeCodeName, param.Name);
                                        continue;
                                    }
                                    builder.AppendLine("__temp = GetParam(\"{0}\");", param.Name);
                                    if (param.ParameterType == typeof(string))
                                        builder.AppendLine("{0} {1} = __temp;", typeCodeName, param.Name);
                                    else
                                    {
                                        //builder.AppendLine("{0} {1} = __temp == null ? default({0}) : ({0})Convert.ChangeType(__temp, typeof({0}));", param.ParameterType.CodeName(), param.Name);
                                        builder.Append("{0} {1} = string.IsNullOrEmpty(__temp) ? default({0}) : ", typeCodeName, param.Name);

                                        var ptype = param.ParameterType;
                                        Type nullable;
                                        if (param.ParameterType.IsNullable(out nullable))
                                        {
                                            typeCodeName = nullable.CodeName();
                                            ptype = nullable;
                                        }

                                        if (ptype == typeof(byte)
                                            || ptype == typeof(sbyte)
                                            || ptype == typeof(ushort)
                                            || ptype == typeof(short)
                                            || ptype == typeof(char)
                                            || ptype == typeof(uint)
                                            || ptype == typeof(int)
                                            || ptype == typeof(float)
                                            || ptype == typeof(ulong)
                                            || ptype == typeof(long)
                                            || ptype == typeof(double))
                                        {
                                            builder.AppendLine("{0}.Parse(__temp);", typeCodeName);
                                        }
                                        //else if (param.ParameterType == typeof(DateTime))
                                        //{
                                        //    builder.AppendLine("Utility.ToTime(long.Parse(__temp));", typeCodeName);
                                        //}
                                        else if (param.ParameterType.IsEnum)
                                        {
                                            builder.AppendLine("({0}){1}.Parse(__temp);", typeCodeName, Enum.GetUnderlyingType(ptype).CodeName());
                                        }
                                        //else if (param.ParameterType == typeof(FileUpload))
                                        //{
                                        //    builder.AppendLine("GetFile(__temp);", typeCodeName);
                                        //}
                                        else
                                        {
                                            builder.AppendLine("JsonReader.Deserialize<{0}>(__temp);", typeCodeName);
                                        }
                                    }
                                }
                                // 日志
                                builder.AppendLine("#if DEBUG");
                                builder.Append("_LOG.Debug(\"{0}", method.Name);
                                for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    if (param.ParameterType.Is(typeof(Delegate)))
                                        continue;
                                    builder.Append(" {0}: {{{1}}}", param.Name, j);
                                    if (j != n)
                                        builder.Append(",");
                                }
                                builder.Append("\"");
                                for (int j = 0; j < parameters.Length; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    if (param.ParameterType.Is(typeof(Delegate)))
                                        continue;
                                    //if (param.ParameterType.Is(dt))
                                    //    builder.AppendFormat(", \"{0}\"", param.ParameterType.CodeName());
                                    if (!param.ParameterType.IsCustomType())
                                        builder.AppendFormat(", {0}", param.Name);
                                    else
                                        builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                                }
                                builder.AppendLine(");");
                                builder.AppendLine("#endif");
                            }
                            //builder.AppendLine("__context.Response.ContentEncoding = CallbackEncoding;");

                            if (method.HasReturnType())
                            {
                                builder.Append("var __result = agent.");
                                builder.AppendMethodInvoke((MethodBase)method);
                                // 自动回调
                                builder.AppendLine("if (__AutoCallback != null) __AutoCallback(__context, __result);");
                                builder.AppendLine("else");
                                builder.AppendBlock(() =>
                                {
                                    builder.AppendLine("string __ret = JsonWriter.Serialize(__result);");
                                    //builder.AppendLine("string __ret = JsonWriter.Serialize({0}, typeof({1}));", parameter.Name, parameter.ParameterType.CodeName());
                                    // 记录日志
                                    builder.AppendLine("#if DEBUG");
                                    builder.AppendLine("_LOG.Debug(\"{0} {{0}}\", __ret);", method.Name);
                                    builder.AppendLine("#endif");
                                    // 回调
                                    builder.AppendLine("this.Response(__ret);");
                                });
                            }
                            else
                            {
                                if (asyncCB.ContainsKey(i))
                                {
                                    // 有委托类型用于回调
                                    builder.AppendLine("var callback = new CB{0}_{1}(this);", type.Name, method.Name);
                                    builder.Append("agent.{0}(", method.Name);
                                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                    {
                                        ParameterInfo param = parameters[j];
                                        if (param.ParameterType.Is(typeof(Delegate)))
                                            builder.Append("callback");
                                        else
                                            builder.Append(param.Name);
                                        if (j != n)
                                            builder.Append(", ");
                                    }
                                    builder.AppendLine(");");
                                }
                                else
                                {
                                    builder.AppendMethodInvoke(method, "agent", "__context", null);
                                }
                            }

                            if (parameters.Length > 0)
                            {
                                foreach (var param in parameters)
                                    if (param.ParameterType == typeof(FileUpload))
                                        builder.AppendLine("{0}.Dispose();", param.Name);
                            }
                        });
                    }
                });
            }

            protected override void WCCallProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                /* 前端调用示例
                 * Interface.Method(param1, param2, (ret) =>
                 * {
                 *     // callback
                 * });
                 */
                string name = type.Name + "Proxy";

                builder.AppendLine("class {0}", name);

                builder.AppendBlock(() =>
                {
                    builder.AppendLine(
@"/// <summary>请求的公共地址，例如 http://127.0.0.1:888/Action/ </summary>
public string Host;
/// <summary>在发送请求之前执行</summary>
public Action<HttpRequestPost> OnSend;
/// <summary>在回调之前执行</summary>
public Action<string> OnCallback;
/// <summary>接口返回HttpError实例或口返回状态码不为200(OnError有指定则仅执行OnError)时</summary>
public Action<int, string> OnHttpError;
/// <summary>请求抛出WebException时</summary>
public Action<HttpRequestPost, WebException> OnError;
/// <summary>回调函数异步还是同步执行，true为异步</summary>
public bool IsAsync = true;

private void _onError(HttpRequestPost req, WebException e, byte[] resultBytes)
{
    AsyncData<string> result = req.Tag as AsyncData<string>;
    if (result != null) result.Error(e);
    if (OnError != null) OnError(req, e);
    else _LOG.Error(e, ""发送请求错误 {0}"", req.Request.RequestUri.ToString());
}");
                    builder.AppendLine();

                    builder.AppendLine(
@"/// <summary>发送HTTP请求</summary>
/// <param name=""url"">请求的地址，例如 1/GetInfo</param>
/// <param name=""data"">请求的参数</param>
/// <param name=""callback"">请求完成后的回调函数</param>
private AsyncData<string> send<T>(string url, string data, Action<T> callback)");

                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine(
@"_LOG.Debug(""{0} {1}"", url, data);

AsyncData<string> result = new AsyncData<string>();

var request = new HttpRequestPost();
request.Tag = result;
request.Connect(Host + url);

if (OnSend != null) OnSend(request);

request.OnError += _onError;
request.OnReceived += (response) =>
{
    if (response.StatusCode == HttpStatusCode.OK)
    {
        string ret = _IO.ReadPreambleText(response.GetResponseStream(), Encoding.UTF8);

        if (ret.StartsWith(""{\""errCode\""""))
        {
            HttpError err = JsonReader.Deserialize<HttpError>(ret);
            if (OnHttpError != null)
            {
                OnHttpError(err.errCode, err.errMsg);
            }
            else
            {
                _LOG.Warning(""未设置错误回调函数: {0} {1}"", err.errCode, err.errMsg);
            }
            result.Error(new HttpException(err.errCode, err.errMsg));
        }
        else
        {
            if (OnCallback != null)
            {
                OnCallback(ret);
            }
            if (callback != null)
            {
                T retObj = JsonReader.Deserialize<T>(ret);
                if (IsAsync)
                {
                    callback(retObj);
                }
                else
                {
                    EntryService.Instance.Synchronize(() => callback(retObj));
                }
            }
            else
            {
                _LOG.Warning(""未设置回调函数: {0}"", ret);
            }
            result.SetData(ret);
        }
    }
    else
    {
        result.Error(new HttpException((int)response.StatusCode, response.StatusDescription));
        if (OnHttpError != null)
        {
            OnHttpError((int)response.StatusCode, response.StatusDescription);
        }
        else
        {
            _LOG.Warning(""未设置错误回调函数: {0} {1}"", response.StatusCode, response.StatusDescription);
        }
    }
};
request.Send(data);

return result;"
                       );
                    });

                    // 通过代理调用接口方法
                    WCCallProxy(builder, call, asyncCB);

                    // 通过代理人处理回调方法
                    WCAgentStub(builder, call, callback, asyncCB);
                });
            }
            protected override void WCCallProxy(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                for (int i = 0; i < call.Length; i++)
                {
                    MethodInfo method = call[i];
                    ParameterInfo[] parameters = method.GetParameters();

                    builder.AppendLine();
                    // 生成注解关联
                    builder.Append("/// <see cref=\"{0}.{1}(", type.FullName, method.Name);
                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                    {
                        if (j != 0)
                            builder.Append(", ");
                        var param = parameters[j];
                        builder.Append("{0}", param.ParameterType.CodeName().Replace('<', '{').Replace('>', '}'));
                    }
                    builder.AppendLine(")\"></see>");
                    // 生成方法头
                    bool hasAsync = asyncCB.ContainsKey(i);
                    if (hasAsync)
                        builder.Append("public AsyncData<string> {0}(", method.Name);
                    else
                        builder.Append("public void {0}(", method.Name);
                    // 生成方法参数
                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                    {
                        if (j != 0)
                            builder.Append(", ");
                        var param = parameters[j];
                        builder.Append("{0} {1}", param.ParameterType.CodeName(), param.Name);
                    }
                    builder.AppendLine(")");
                    builder.AppendBlock(() =>
                    {
                        // 处理参数为HTTP请求参数
                        builder.AppendLine("var parameters = new StringBuilder();");
                        for (int j = 0, n = parameters.Length - 2; j <= n; j++)
                        {
                            builder.Append("parameters.Append(\"{0}={{0}}", parameters[j].Name);
                            if (j != n)
                                builder.Append("&");
                            builder.Append("\", ");
                            if (parameters[j].ParameterType.IsEnum)
                                builder.Append("({0})", parameters[j].ParameterType.GetEnumUnderlyingType().CodeName());
                            if (parameters[j].ParameterType.IsCustomType())
                                builder.Append("JsonWriter.Serialize({0})", parameters[j].Name);
                            else
                                builder.Append("{0}", parameters[j].Name);
                            builder.AppendLine(");");
                        }
                        // 生成请求调用
                        if (hasAsync)
                            builder.AppendLine("return send(\"{0}/{1}\", parameters.ToString(), {2});", agent.Protocol, method.Name, parameters.Length == 0 ? "(Action<string>)null" : parameters[parameters.Length - 1].Name);
                        else
                            builder.AppendLine("send(\"{0}/{1}\", parameters.ToString(), {2});", agent.Protocol, method.Name, parameters.Length == 0 ? "(Action<string>)null" : parameters[parameters.Length - 1].Name);
                    });
                }
                builder.AppendLine();
            }
            protected override void WCAgentStub(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                // 前端不需要管STUB，而是只管收到了什么消息
            }
            protected override void WCDefaultAgentStub(StringBuilder builder, MethodInfo[] callback)
            {
            }
        }
        /// <summary>C#注释生成的XML文档</summary>
        class CSharpXML
        {
            private static Member Empty = new Member();
            public struct Member
            {
                private static Dictionary<string, string> empty1 = new Dictionary<string, string>();

                public XmlNode Node { get; private set; }

                public Member(XmlNode node)
                {
                    this.Node = node;
                }

                /// <summary>summary</summary>
                public string GetSummary()
                {
                    string[] lines = GetSummaries();
                    if (lines == null)
                        return null;
                    return string.Join("\r\n", lines);
                }
                /// <summary> * summary \r\n</summary>
                public string GetSummaryStar()
                {
                    string[] lines = GetSummaries();
                    if (lines == null)
                        return null;
                    return " * " + string.Join("\r\n", lines) + "\r\n";
                }
                /// <summary>
                /// summary
                /// * summary
                /// * summary
                /// </summary>
                public string[] GetSummaries()
                {
                    if (Node == null)
                        return null;
                    var node = Node.FirstOrDefault(i => i.Name == "summary");
                    if (node == null)
                        return null;
                    string value = node.Value.Trim();
                    var lines = value.Split('\n');
                    for (int i = 1; i < lines.Length; i++)
                        lines[i] = " * " + lines[i].Trim();
                    return lines;
                }
                /// <summary>/** summary */\r\n</summary>
                public string GetFullSummary()
                {
                    string summary = GetSummary();
                    if (string.IsNullOrEmpty(summary))
                        return null;
                    return "/** " + summary + " */\r\n";
                }
                /// <summary>name, summary</summary>
                public Dictionary<string, string> GetParams()
                {
                    if (Node == null)
                        return empty1;
                    return Node.Where(i => i.Name == "param").ToDictionary(i => i.Attributes["name"], i => i.Value);
                }
                public string GetReturn()
                {
                    if (Node == null)
                        return null;
                    return Node.FirstOrDefault(i => i.Name == "returns")?.Value;
                }
            }

            public XmlNode Members { get; private set; }

            public CSharpXML(XmlNode members)
            {
                this.Members = members;
            }

            /// <summary>查找member的注释</summary>
            /// <param name="key">C#文档的member.name，T:类型 F:字段 M:方法</param>
            public Member Find(string key)
            {
                if (Members == null)
                    return Empty;
                Func<XmlNode, bool> match;
                if (key[0] == 'M')
                    match = (i => i.Attributes["name"].StartsWith(key));
                else
                    match = (i => i.Attributes["name"] == key);
                return new Member(Members.FirstOrDefault(match));
            }
            public Member Find(Type type)
            {
                return Find("T:" + type.Name);
            }
            public Member Find(Type type, FieldInfo member)
            {
                return Find("F:" + type.Name + "." + member.Name);
            }
            public Member Find(Type type, MethodInfo member)
            {
                return Find("M:" + type.Name + "." + member.Name);
            }

            public static CSharpXML Read(string file)
            {
                XmlNode root = new XmlReader(File.ReadAllText(file)).ReadToNode();
                return new CSharpXML(root["members"]);
            }
        }
        static string TypeToTs(Type type)
        {
            if (type == typeof(FileUpload))
            {
                return "File";
            }

            Type nullable;
            if (type.IsValueType && type.IsNullable(out nullable))
            {
                // Nullable<struct>
                return TypeToTs(nullable) + '?';
            }

            if (type == typeof(char)
                || type == typeof(string)
                || type == typeof(DateTime))
            {
                return "string";
            }
            else if (type == typeof(bool))
            {
                return "boolean";
            }
            else if (type == typeof(sbyte)
                || type == typeof(byte)
                || type == typeof(short)
                || type == typeof(ushort)
                || type == typeof(int)
                || type == typeof(uint)
                || type == typeof(float)
                || type == typeof(long)
                || type == typeof(ulong)
                || type == typeof(double)
                )
            {
                return "number";
            }

            if (type.Is(typeof(Dictionary<,>)))
            {
                var args = type.GetGenericArguments();
                return string.Format("Record<{0}, {1}>", TypeToTs(args[0]), TypeToTs(args[1]));
            }

            bool isList = !type.IsArray && type.Is(typeof(IList));
            if (type.IsArray || isList)
            {
                Type elementType;
                if (isList)
                    elementType = type.GetGenericArguments()[0];
                else
                    elementType = type.GetElementType();
                return TypeToTs(elementType) + "[]";
            }

            if (type.IsGenericType)
            {
                return string.Format("{0}<{1}>", type.Name.Substring(0, type.Name.IndexOf("`")), string.Join(", ", type.GetGenericArguments().Select(i => GetTSType(i))));
            }

            return type.Name;
        }
        [Obsolete("交互协议暂时使用了JSDoc，没有使用TS了")]
        static void BuildTypeToTs(Type type, StringBuilder builder, XmlNode members)
        {
            Type nullable;
            if (type.IsValueType && type.IsNullable(out nullable))
            {
                BuildTypeToTs(nullable, builder, members);
            }

            if (type.IsEnum)
            {
                BuildJSDoc(builder, "T:" + type.Name, members);
                builder.AppendLine("declare enum {0}", type.Name);
                builder.AppendBlock(() =>
                {
                    var names = Enum.GetNames(type);
                    var values = Enum.GetValues(type);
                    Type utype = Enum.GetUnderlyingType(type);
                    for (int i = 0; i < names.Length; i++)
                    {
                        BuildJSDoc(builder, "F:" + type.Name + "." + names[i], members);
                        builder.AppendLine("{0} = {1},", names[i], Convert.ChangeType(values.GetValue(i), utype));
                    }
                });
            }

            bool isList = !type.IsArray && type.Is(typeof(IList));
            if (type.IsArray || isList)
            {
                Type elementType;
                if (isList)
                    elementType = type.GetGenericArguments()[0];
                else
                    elementType = type.GetElementType();
                BuildTypeToTs(type, builder, members);
            }

            if (type.IsCustomType())
            {
                var fields = type.GetFields();
                if (fields.Length == 0)
                    return;
                BuildJSDoc(builder, "T:" + type.Name, members);
                builder.AppendLine("declare interface {0}", type.Name);
                builder.AppendBlock(() =>
                {
                    foreach (var field in fields)
                    {
                        BuildJSDoc(builder, "F:" + type.Name + "." + field.Name, members);
                        builder.AppendLine("{0}:{1};", field.Name, TypeToTs(field.FieldType));
                    }
                });
            }
        }
        static void BuildTypeToJsDoc(HashSet<Type> types, Type type, StringBuilder builder, CSharpXML members)
        {
            if (!types.Add(type))
                return;

            Type nullable;
            if (type.IsValueType && type.IsNullable(out nullable))
            {
                BuildTypeToJsDoc(types, nullable, builder, members);
            }

            if (type.IsEnum)
            {
                builder.AppendLine("/**");
                builder.Append(" * @typedef {(");
                var names = Enum.GetNames(type);
                var values = Enum.GetValues(type);
                Type utype = Enum.GetUnderlyingType(type);
                for (int i = 0, e = names.Length - 1; i <= e; i++)
                {
                    builder.Append("'{0}'", Convert.ChangeType(values.GetValue(i), utype));
                    if (i != e)
                        builder.Append(" | ");
                }
                builder.Append(")}} {0}", type.Name);
                string summary = members.Find("T:" + type.Name).GetSummary();
                if (!string.IsNullOrEmpty(summary))
                    builder.Append(" - {0}", summary);
                builder.AppendLine();
                builder.AppendLine(" */");
                builder.AppendLine("export const {0} =", type.Name);
                builder.AppendBlock(() =>
                {
                    for (int i = 0; i < names.Length; i++)
                    {
                        builder.Append(members.Find("F:" + type.Name + "." + names[i]).GetFullSummary());
                        builder.AppendLine("{0}: {1},", names[i], Convert.ChangeType(values.GetValue(i), utype));
                        builder.AppendLine("{0}: '{1}',", Convert.ChangeType(values.GetValue(i), utype), names[i]);
                    }
                });
            }

            if (type.Is(typeof(Dictionary<,>)))
                BuildTypeToJsDoc(types, type.GetGenericArguments()[1], builder, members);

            bool isList = !type.IsArray && type.Is(typeof(IList));
            if (type.IsArray || isList)
            {
                Type elementType;
                if (isList)
                    elementType = type.GetGenericArguments()[0];
                else
                    elementType = type.GetElementType();
                BuildTypeToJsDoc(types, elementType, builder, members);
            }

            if (type.IsCustomType())
            {
                HashSet<Type> temp = new HashSet<Type>();

                bool generic = type.IsGenericType;
                if (generic)
                {
                    var gargs = type.GetGenericArguments();
                    foreach (var item in gargs)
                        temp.Add(item);
                    type = type.GetGenericTypeDefinition();
                }

                if (!generic || types.Add(type))
                {
                    var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
                    if (fields.Length == 0)
                        return;
                    builder.AppendLine("/**");
                    if (generic)
                    {
                        var generics = type.GetGenericArguments();
                        foreach (var g in generics)
                            builder.AppendLine(" * @template {0}", TypeToTs(g));
                    }
                    builder.Append(members.Find("T:" + type.Name).GetSummaryStar());
                    builder.AppendLine(" * @typedef {{object}} {0}", generic ? type.Name.Substring(0, type.Name.IndexOf("`")) : type.Name);
                    foreach (var field in fields)
                    {
                        temp.Add(field.FieldType);

                        builder.Append(" * @property {{{0}}} [{1}]", TypeToTs(field.FieldType), field.Name);
                        string s = members.Find("F:" + type.Name + "." + field.Name).GetSummary();
                        if (!string.IsNullOrEmpty(s))
                            builder.Append(" - {0}", s);
                        builder.AppendLine();
                    }
                    builder.AppendLine(" */");
                }

                foreach (var item in temp)
                    BuildTypeToJsDoc(types, item, builder, members);
            }
        }
        /// <summary>生成JSDoc文档格式的注释</summary>
        /// <param name="key">C#文档的member.name，T:类型 F:字段 M:方法</param>
        /// <param name="members">C#文档的members标签</param>
        /// <param name="member">成员，包含类型等信息</param>
        static void BuildJSDoc(StringBuilder builder, string key, XmlNode members)
        {
            if (members == null)
                return;
            Func<XmlNode, bool> match;
            if (key[0] == 'M')
                match = (i => i.Attributes["name"].StartsWith(key));
            else
                match = (i => i.Attributes["name"] == key);
            var find = members.FirstOrDefault(match);
            if (find == null)
                return;
            if (find.ChildCount == 1 && find[0].Name == "summary")
            {
                builder.Append("/** ");
                AppendSummary(builder, find[0]);
                builder.AppendLine(" */");
            }
            else
            {
                builder.AppendLine("/**");
                foreach (var item in find)
                {
                    builder.Append("* ");
                    if (item.Name == "summary")
                    {
                        AppendSummary(builder, item);
                        builder.AppendLine();
                    }
                    else if (item.Name == "param")
                        builder.AppendLine("@param [{0}] - {1}", item.Attributes["name"], item.Value);
                    else if (item.Name == "returns")
                        builder.AppendLine("@returns {0}", item.Value);
                }
                builder.AppendLine(" */");
            }
        }
        static void AppendSummary(StringBuilder builder, XmlNode summary)
        {
            string value = summary.Value.Trim();
            var lines = value.Split('\n');
            builder.Append(lines[0].Trim());
            if (lines.Length > 1)
            {
                builder.AppendLine("");
                for (int i = 1; i < lines.Length; i++)
                    builder.AppendLine("* {0}", lines[i].Trim());
            }
        }
        /// <summary>XMLHttpRequest</summary>
        class ProtocolHttpJS : ProtocolHttp
        {
            /// <summary>是否需要模块化JS：模块化使用export / 非模块化使用var</summary>
            [Obsolete("统一模块化编码，要非模块化可以到JS层面转换")]
            public bool IsModule;

            public StringBuilder TypeBuilder = new StringBuilder();

            protected string GetTypeShortName()
            {
                string name = type.Name;
                if (name.Length > 2)
                    name = name.Substring(0, 2);
                return name.ToLower();
            }

            protected override string ClientSuffix { get { return "js"; } }

            protected override void Write(MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                WNamespace(server);

                WSInterface(server, call, asyncCB);
                WSCallbackType(server, call, asyncCB);
                WSCallbackProxy(server, call, callback, asyncCB);
                WSAgentStub(server, call, asyncCB);

                WCCallProxy(builder, call, callback, asyncCB);
            }
            protected override void WCCallProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                string xml = Path.Combine(Path.ChangeExtension(type.Assembly.Location, ".xml"));
                XmlNode members = null;
                if (File.Exists(xml))
                {
                    Console.WriteLine("协议包含注释文件：{0}", xml);
                    XmlNode root = new XmlReader(File.ReadAllText(xml)).ReadToNode();
                    members = root["members"];
                }
                CSharpXML doc = new CSharpXML(members);
                HashSet<Type> types = new HashSet<Type>();

                // 生成过的类型
                for (int i = 0; i < call.Length; i++)
                {
                    ParameterInfo[] parameters = call[i].GetParameters();
                    bool hasAsync = asyncCB.ContainsKey(i);

                    foreach (var param in parameters.Concat(new ParameterInfo[] { call[i].ReturnParameter }))
                    {
                        var type = param.ParameterType;
                        if (hasAsync && param.ParameterType.IsDelegate())
                            type = param.ParameterType.GetGenericArguments()[0];
                        // 生成类型
                        BuildTypeToJsDoc(types, type, TypeBuilder, doc);
                    }
                }

                string name = GetTypeShortName();
                builder.Append(doc.Find(type).GetFullSummary());
                builder.AppendLine("const {0} =", name);
                builder.AppendBlockWithEnd(() =>
                {
                    builder.AppendLine(
@"/** @param {import('vue').App} vue */
install: (vue) => vue.config.globalProperties.{api} = {api},
/** 后端接口的URL */
url:  '',
/** 状态码对应错误信息，错误码和错误信息的对象会回调给event.onerror事件 */
error: { 0: '请求已中止' },
/**
 * @typedef {Object} xhr_ex
 * @property {string} url - 接口的相对 url
 * @property {XMLHttpRequestBodyInit} data - 发送接口的数据
 * @property {('POST' | 'GET' | 'PUT' | 'DELETE')} method - 发送接口的方法
 * @property {function():Promise} resend - 重发接口，并返回重发的结果来完成 Promise
 */
/** @typedef {XMLHttpRequest & xhr_ex} xhr */
/** 请求结果
 * @callback RequestResult
 * @param {{ errCode: number, errMsg: string } | string} data - 请求返回的结果
 * @param {xhr} [xhr] - 重发请求
 * @param {function(any)} [resolve] - 回复成功
 * @param {function(any)} [reject] - 回复失败
 * @returns {Promise<any | undefined> | (any | undefined)} 返回非空则不会继续后面的处理
 */
/**
 * @typedef {Object} RequestEvent
 * @property {function(xhr)} [onopen] - 请求准备发送时的事件
 * @property {function(xhr)} [onloading] - 请求正在加载的事件(readyState = 3)
 * @property {function(xhr)} [oncomplete] - 请求完成时的事件(readyState = 4)
 * @property {RequestResult} [onsuccess] - 请求成功的事件(readyState = 4 && status == 200)
 * @property {RequestResult} [onerror] - 请求发生错误时的事件(readyState = 4 && status != 200)
 * @property {function(ProgressEvent)} [onprogress] - 上传文件请求的进度事件
 */
/** 接口请求的全局事件
 * @type {RequestEvent} */
__event: undefined,
/** 接口请求的全局事件
 * @type {RequestEvent} */
event: undefined,
/** 本次接口请求的临时事件，onprogress以外的全局事件任然会触发，临时事件优先于全局事件触发
 * @param {RequestEvent} event */
eventonce(event)
{
    this.__event = Object.assign({}, this.__event, event);
    return this;
},
/** 发送接口
 * @param {string} url - 接口url
 * @param {XMLHttpRequestBodyInit} [data] - 数据，可以是FormData或a=1&b=2格式的字符串
 * @param {('POST' | 'GET')} method - 默认POST
 */
send(url, data, method = 'POST')
{
    return new Promise((resolve, reject) => 
    {
        const req = new XMLHttpRequest();
        const event = this.__event;
        this.__event = undefined;
        req.url = url;
        req.data = data;
        req.method = method;
        req.resend = () => { return this.eventonce(event).send(url, data, method).then(i => resolve(i)).catch(i => reject(i)); };
        req.onreadystatechange = async () => 
        {
            if (req.readyState == 4)
            {
                event?.oncomplete?.(req) | this.event?.oncomplete?.(req);
                if (req.status == 200)
                {
                    let obj = null;
                    switch (req.responseType)
                    {
                        case 'text': if (req.response) obj = JSON.parse(req.responseText); break;
                        case 'blob':
                        if (req.response.type == 'text/plain') await req.response.text().then((value) => obj = JSON.parse(value));
                        else obj = req.response;
                        break;
                    }
                    if (obj && obj.errCode)
                        await event?.onerror?.(obj, req, resolve, reject) || await this.event?.onerror?.(obj, req, resolve, reject) | reject(obj);
                    else
                        await event?.onsuccess?.(obj, req, resolve, reject) || await this.event?.onsuccess?.(obj, req, resolve, reject) | resolve(obj);
                }
                else
                {
                    let error = { errCode: req.status, errMsg: '' };
                    error.errMsg = this.error[req.status] || req.statusText || req.responseText;
                    await event?.onerror?.(error, req, resolve, reject) || this.event?.onerror?.(error, req, resolve, reject) || reject(error);
                }
            }
            else if (req.readyState == 3) event?.onloading?.(req) | this.event?.onloading?.(req);
        }
        req.open(method, this.url + url, true);
        req.upload.onprogress = event?.onprogress || this.event?.onprogress;
        req.responseType = 'text';
        if (!data || data.constructor == String)
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
        if (event?.onopen) event.onopen(req);
        else if (this.event?.onopen) this.event.onopen(req);
        req.send(data);
    });
},
/** 调用下载文件的接口前调用
 * @param {string} filename - 下载文件的名字，例如download.csv
 * @example
 * api.download('download.csv').downloadCSV()
 */
download(filename)
{
    return this.eventonce(
    {
        oncomplete(req)
        {
            const a = window.document.createElement('a');
            a.href = URL.createObjectURL(req.response);
            a.download = filename;
            a.click();
        }
    })
},".Replace("{api}", name));
                    // 通过代理调用接口方法
                    //WCCallProxy(builder, call, asyncCB);
                    for (int i = 0; i < call.Length; i++)
                    {
                        MethodInfo method = call[i];
                        ParameterInfo[] parameters = method.GetParameters();

                        // 方法头
                        bool hasAsync = asyncCB.ContainsKey(i);
                        int pcount = parameters.Where(j => !hasAsync || !j.ParameterType.IsDelegate()).Count();
                        Type returnType = method.ReturnType;
                        var member = doc.Find(type, method);
                        var _params = member.GetParams();
                        var _return = member.GetReturn();

                        if (pcount > 1)
                        {
                            // 大于一个参数，第一个参数的类型允许是一个object
                            // 这里声明这个object类型
                            TypeBuilder.AppendLine("/**");
                            TypeBuilder.AppendLine(" * @typedef {0}", method.Name);
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                var param = parameters[j];
                                if (hasAsync && param.ParameterType.IsDelegate())
                                {
                                    returnType = param.ParameterType.GenericTypeArguments[0];
                                    if (string.IsNullOrEmpty(_return))
                                    {
                                        // 回调参数的注释变成返回值的注释
                                        _params.TryGetValue(param.Name, out _return);
                                    }
                                    continue;
                                }
                                TypeBuilder.Append(" * @property {{{0}}} [{1}]", TypeToTs(param.ParameterType), param.Name);
                                string s;
                                if (_params.TryGetValue(param.Name, out s))
                                    TypeBuilder.Append(" - {0}", s);
                                TypeBuilder.AppendLine();
                            }
                            TypeBuilder.AppendLine(" */");
                        }

                        // 方法注释
                        //BuildJSDoc(builder, string.Format("M:{0}.{1}", type.Name, method.Name), members);
                        builder.AppendLine("/**");
                        builder.Append(member.GetSummaryStar());
                        for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                        {
                            var param = parameters[j];
                            if (hasAsync && param.ParameterType.IsDelegate())
                            {
                                returnType = param.ParameterType.GenericTypeArguments[0];
                                continue;
                            }
                            builder.Append(" * @param {{{0}}} [{1}]", TypeToTs(param.ParameterType) + (j == 0 && pcount > 1 ? " | " + method.Name : ""), param.Name);
                            string s;
                            if (_params.TryGetValue(param.Name, out s))
                                builder.Append(" - {0}", s);
                            builder.AppendLine();
                            // 生成第一个参数类型的example，方便复制到组件的data中
                            //if (j == 0 && pcount > 1)
                            //{
                            //    builder.AppendLine(" * @example");
                            //    builder.AppendLine(" * {");
                            //    for (int k = 0; k <= n; k++)
                            //    {
                            //        var param2 = parameters[k];
                            //        if (hasAsync && param2.ParameterType.IsDelegate())
                            //            continue;
                            //        builder.AppendLine(" *   {0}: undefined,", param2.Name);
                            //    }
                            //    builder.AppendLine(" * }");
                            //}
                        }
                        builder.Append(" * @returns {{Promise<{0}>}}", returnType.FullName == "System.Void" ? "any" : TypeToTs(returnType));
                        if (!string.IsNullOrEmpty(_return))
                            builder.Append(" - {0}", _return);
                        builder.AppendLine();
                        builder.AppendLine(" */");
                        builder.Append("{0}(", method.Name);
                        // 分别传每个值
                        for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                        {
                            var param = parameters[j];
                            if (hasAsync && param.ParameterType.IsDelegate())
                                continue;
                            if (j != 0)
                                builder.Append(", ");
                            builder.Append("{0}", param.Name);
                            //builder.Append(":{0}", TypeToTs(param.ParameterType));
                            // 大于一个参数，第一个参数的类型允许是一个object
                            //if (j == 0 && pcount > 1)
                            //{
                            //    builder.Append("|{");
                            //    for (int k = 0; k <= n; k++)
                            //    {
                            //        var param2 = parameters[k];
                            //        if (hasAsync && param2.ParameterType.IsDelegate())
                            //            continue;
                            //        if (k != 0)
                            //            builder.Append(", ");
                            //        builder.Append("{0}", param2.Name);
                            //        builder.Append(":{0}", TypeToTs(param2.ParameterType));
                            //    }
                            //    builder.Append("}");
                            //}
                        }
                        // 后端接口没有回调，这里固定一个回调函数
                        //if (!hasAsync)
                        //{
                        //    if (parameters.Length > 0)
                        //        builder.Append(", ");
                        //    builder.Append("callback");
                        //}
                        // 直接传对象
                        //builder.Append("data, callback");
                        //builder.AppendLine("):Promise<{0}>", returnType.FullName == "System.Void" ? "any" : TypeToTs(returnType));
                        builder.AppendLine(")");
                        builder.AppendBlockWithComma(() =>
                        {
                            bool hasFileUpload = parameters.Any(j => j.ParameterType == typeof(FileUpload));
                            string callbackName = "callback";
                            if (pcount > 0)
                            {
                                // 参数大于1个的允许传一个对象
                                if (pcount > 1)
                                {
                                    builder.Append("arguments.length == 1 && ({ ");
                                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                    {
                                        var param = parameters[j];
                                        if (hasAsync && param.ParameterType.IsDelegate())
                                            continue;
                                        if (j != 0)
                                            builder.Append(", ");
                                        builder.Append("{0}", param.Name);
                                    }
                                    builder.AppendLine(" } = arguments[0])");
                                }

                                if (hasFileUpload)
                                    builder.AppendLine("const data = new FormData();");
                                else
                                    builder.AppendLine("const data = [];");
                            }
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                var param = parameters[j];
                                if (hasAsync && param.ParameterType.IsDelegate())
                                {
                                    callbackName = param.Name;
                                    continue;
                                }

                                builder.Append("if ({0}) ", param.Name);
                                if (hasFileUpload)
                                {
                                    builder.Append("data.append(\"{0}\", ", param.Name);
                                }
                                else
                                {
                                    builder.Append("data.push(\"{0}=\" + ", param.Name);
                                }
                                if (param.ParameterType == typeof(FileUpload))
                                    builder.Append("{0}, {0}.name", param.Name);
                                else if (param.ParameterType == typeof(string))
                                    builder.Append("encodeURIComponent({0})", param.Name);
                                // 可能传时间戳，可能传字符串，可能传Date对象
                                else if (param.ParameterType == typeof(DateTime))
                                    builder.Append("({0}.toLocaleString?.() || {0})", param.Name);
                                else if (param.ParameterType.IsCustomType())
                                    builder.Append("encodeURIComponent(JSON.stringify({0}))", param.Name);
                                else
                                    builder.Append("{0}", param.Name);
                                builder.AppendLine(");");
                            }
                            builder.AppendLine("return this.send(\"{0}/{1}\"{2});", agent.Protocol, method.Name,
                                pcount > 0 ? (hasFileUpload ? ", data" : ", data.join('&')") : "");
                        });
                    }
                });

                //if (IsModule)
                {
                    builder.AppendLine("export default {0}", name);
                }
            }
            protected override void WCCallProxy(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
            }

            public override void Save()
            {
                SaveCode(Path.Combine(OutputClientPath, type.Name + ".d.ts"),
@"import {api} from './{api2}Proxy'

declare module '@vue/runtime-core'
{
    interface ComponentCustomProperties
    {
        {api}: typeof {api},
    }
}".Replace("{api}", GetTypeShortName()).Replace("{api2}", type.Name));
                builder.AppendLine(TypeBuilder.ToString());
                TypeBuilder = null;
                base.Save();
            }
        }
        /// <summary>WebSocket用</summary>
        class ProtocolJsonJs : ProtocolDefault
        {
            /// <summary>是否需要模块化JS：模块化使用export / 非模块化使用var</summary>
            [Obsolete("统一模块化编码，要非模块化可以到JS层面转换")]
            public bool IsModule;

            protected override string ClientSuffix { get { return "js"; } }

            protected override void Write(MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                WNamespace(server);

                WSInterface(server, call, asyncCB);
                WSCallbackType(server, call, asyncCB);
                WSCallbackProxy(server, call, callback, asyncCB);
                WSAgentStub(server, call, asyncCB);

                WCCallProxy(builder, call, callback, asyncCB);
            }

            protected override void WNamespace(StringBuilder builder)
            {
                builder.AppendLine("using System.Text;");
                base.WNamespace(builder);
            }
            protected override void WSCallbackType(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                if (asyncCB.Count > 0)
                    throw new InvalidOperationException("WebSocket暂不支持回调式的调用");
            }
            protected override void WSCallbackProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                // 生成调用类型
                for (int i = 0, len = call.Length; i < len; i++)
                {
                    MethodInfo method = call[i];
                    ParameterInfo[] parameters = method.GetParameters();
                    if (parameters.Length == 0) continue;
                    // 回调参数生成类型
                    builder.AppendLine("class {0}_{1}", type.Name, method.Name);
                    builder.AppendBlock(() =>
                    {
                        for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                        {
                            var param = parameters[j];
                            builder.AppendLine("public {0} {1};", param.ParameterType.CodeName(), param.Name);
                        }
                    });
                }

                // 生成回调类型
                WriteCallbackObject(builder, callback);

                if (agent.Callback == null)
                    builder.AppendLine("static class {0}CallbackProxy", type.Name);
                else
                    builder.AppendLine("static class {0}Proxy", agent.Callback.Name);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("public static Func<object, byte[]> __Serialize = (obj) => Encoding.UTF8.GetBytes(JsonWriter.Serialize(obj));");
                    for (int i = 0, len = callback.Length; i < len; i++)
                    {
                        // 调用中含有委托参数的方法和回调接口都需要生成回调函数
                        MethodInfo method = callback[i];

                        // 方法头
                        ParameterInfo[] parameters = method.GetParameters();
                        builder.Append("public static {0} {1}(Link __link", method.ReturnType.CodeName(), method.Name);
                        foreach (ParameterInfo param in parameters)
                            builder.AppendFormat(", {0} {1}", param.ParameterType.CodeName(), param.Name);
                        builder.AppendLine(")");

                        // 方法体
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (__link == null || !__link.IsConnected) return;");
                            builder.AppendLine("var __cb = new RET_{0}_{1}();", type.Name, method.Name);
                            // 协议
                            builder.AppendLine("__cb.Protocol = ((byte){0});", agent.Protocol);
                            builder.AppendLine("__cb.Stub = ((ushort){0});", i);
                            // 回调参数内容
                            foreach (ParameterInfo param in parameters)
                                builder.AppendLine("__cb.{0} = {0};", param.Name);
                            builder.AppendLine("var __buffer = __Serialize(__cb);");
                            // 日志
                            builder.AppendLine("#if DEBUG");
                            builder.AppendFormat("_LOG.Debug(\"{0}({{0}} bytes)", method.Name);
                            for (int j = 0, m = parameters.Length - 1; j <= m; j++)
                            {
                                ParameterInfo param = parameters[j];
                                builder.Append(" {0}: {{{1}}}", param.Name, j + 1);
                                if (j != m)
                                    builder.Append(",");
                            }
                            builder.Append("\", __buffer.Length");
                            for (int j = 0, m = parameters.Length - 1; j <= m; j++)
                            {
                                ParameterInfo param = parameters[j];
                                if (!param.ParameterType.IsCustomType())
                                    builder.AppendFormat(", {0}", param.Name);
                                else
                                    builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                            }
                            builder.AppendLine(");");
                            builder.AppendLine("#endif");
                            builder.AppendLine("__link.Write(__buffer);");
                        });
                    }
                });
                builder.AppendLine();
            }
            protected override void WSAgentStub(StringBuilder builder, MethodInfo[] call, Dictionary<int, Type> asyncCB)
            {
                bool hasAgent = !string.IsNullOrEmpty(agent.AgentType);

                string name = type.Name + "Stub";
                builder.AppendLine("class {0} : {1}", name, typeof(StubJson).Name);
                builder.AppendBlock(() =>
                {
                    string name2 = hasAgent ? agent.AgentType : "_" + type.Name;

                    builder.AppendLine("public _{0} __Agent;", type.Name);
                    builder.AppendLine("public Func<_{0}> __GetAgent;", type.Name);
                    builder.AppendLine("public Func<string, {0}> __ReadAgent;", name2);
                    builder.AppendLine("public {0}(_{1} agent) : base({2})", name, type.Name, agent.Protocol);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__Agent = agent;");
                        foreach (var method in call)
                            builder.AppendLine("AddMethod({0});", method.Name);
                    });
                    builder.AppendLine("public {0}(Func<_{1}> agent) : this((_{1})null)", name, type.Name, agent.Protocol);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__GetAgent = agent;");
                    });
                    builder.AppendLine("public {0}(Func<string, {1}> agent) : this((_{2})null)", name, name2, type.Name);
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("this.__ReadAgent = agent;");
                    });
                    // 根据方法参数生成对应的处理方法存根
                    for (int i = 0; i < call.Length; i++)
                    {
                        MethodInfo method = call[i];
                        bool isDelegate = asyncCB.ContainsKey(i);
                        // 方法头
                        builder.AppendLine("{0} {1}(string __stream)", method.ReturnType.CodeName(), method.Name);

                        ParameterInfo[] parameters = method.GetParameters();
                        builder.AppendBlock(() =>
                        {
                            //builder.AppendLine("Timer timer = Timer.StartNew();");    // 统计方法调用次数，可用于分析用户操作
                            builder.AppendLine("var agent = __Agent;");
                            builder.AppendLine("if (__GetAgent != null) { var temp = __GetAgent(); if (temp != null) agent = temp; }");
                            if (hasAgent)
                            {
                                // 指定了代理人类型时，处理方法首个参数将可由读取委托来获得
                                builder.AppendLine("var __client = default({0});", agent.AgentType);
                                builder.AppendLine("if (__ReadAgent != null) { var temp = __ReadAgent(__stream);if (temp != null) __client = temp; }");
                            }
                            else
                            {
                                // 没有指定代理人类型时，将采用默认代理人
                                builder.AppendLine("if (__ReadAgent != null) { var temp = __ReadAgent(__stream); if (temp != null) agent = temp; }");
                            }
                            if (parameters.Length > 0)
                            {
                                // 参数声明
                                builder.AppendLine("var __obj = JsonReader.Deserialize<{0}_{1}>(__stream);", type.Name, method.Name);
                                foreach (ParameterInfo param in parameters)
                                {
                                    if (isDelegate && param.ParameterType.Is(typeof(Delegate)))
                                    {
                                        builder.AppendLine("byte {0};", param.Name);
                                        name = param.Name;
                                    }
                                    else
                                        builder.AppendLine("{0} {1};", param.ParameterType.CodeName(), param.Name);
                                }
                                // 参数读取赋值
                                foreach (ParameterInfo param in parameters)
                                    builder.AppendLine("{0} = __obj.{0};", param.Name);
                                // 日志
                                builder.AppendLine("#if DEBUG");
                                builder.AppendFormat("_LOG.Debug(\"{0}", method.Name);
                                for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    builder.AppendFormat(" {0}: {{{1}}}", param.Name, j);
                                    if (j != n)
                                        builder.Append(",");
                                }
                                builder.Append("\"");
                                for (int j = 0; j < parameters.Length; j++)
                                {
                                    ParameterInfo param = parameters[j];
                                    if (param.ParameterType.Is(typeof(Delegate)))
                                        builder.AppendFormat(", \"{0}\"", param.ParameterType.CodeName());
                                    else if (!param.ParameterType.IsCustomType())
                                        builder.AppendFormat(", {0}", param.Name);
                                    else
                                        builder.AppendFormat(", JsonWriter.Serialize({0})", param.Name);
                                }
                                builder.AppendLine(");");
                                builder.AppendLine("#endif");
                            }
                            builder.AppendFormat("agent.{0}({1}", method.Name, hasAgent ? (parameters.Length == 0 ? "__client" : "__client, ") : "");
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                ParameterInfo param = parameters[j];
                                builder.Append("{0}", param.Name);
                                if (j != n)
                                    builder.Append(", ");
                            }
                            builder.AppendLine(");");
                        });
                    }
                });
            }

            protected override void WCCallProxy(StringBuilder builder, MethodInfo[] call, MethodInfo[] callback, Dictionary<int, Type> asyncCB)
            {
                string name = type.Name + "Proxy";
                if (IsModule)
                {
                    builder.AppendLine("const {0} = {{}};", name);
                    builder.AppendLine("export default {0};", name);
                }
                else
                {
                    builder.AppendLine("var {0} = {{}};", name);
                }
                builder.AppendLine("{0}.ws = null;", name);
                builder.AppendLine("{0}.onError = null;", name);
                foreach (var method in callback)
                {
                    builder.Append("{0}.{1} = function(", name, method.Name);
                    ParameterInfo[] parameters = method.GetParameters();
                    for (int i = 0, n = parameters.Length - 1; i <= n; i++)
                    {
                        builder.Append(parameters[i].Name);
                        if (i != n)
                            builder.Append(", ");
                    }
                    builder.AppendLine("){};");
                }
                // 收到服务端回调的处理
                builder.AppendLine("{0}.callback = function(ret)", name);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("if (ret.errCode && {0}.onError) {0}.onError(ret);", name);
                    builder.AppendLine("if (ret.Protocol != {0}) return;", agent.Protocol);
                    builder.AppendLine("switch (ret.Stub)");
                    builder.AppendBlock(() =>
                    {
                        for (int i = 0; i < callback.Length; i++)
                        {
                            var method = callback[i];
                            builder.Append("case {0}: if ({1}.{2}) {1}.{2}(", i, name, method.Name);
                            ParameterInfo[] parameters = method.GetParameters();
                            for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                            {
                                builder.AppendFormat("ret.{0}", parameters[j].Name);
                                if (j != n)
                                    builder.Append(", ");
                            }
                            builder.AppendLine("); break;");
                        }
                    });
                });
                // 通过代理调用接口方法
                //WCCallProxy(builder, call, asyncCB);
                for (int i = 0; i < call.Length; i++)
                {
                    MethodInfo method = call[i];
                    ParameterInfo[] parameters = method.GetParameters();

                    // 方法头
                    bool hasAsync = asyncCB.ContainsKey(i);
                    builder.Append("{0}.{1} = function(", name, method.Name);
                    // 分别传每个值
                    for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                    {
                        if (j != 0)
                            builder.Append(", ");
                        var param = parameters[j];
                        builder.Append("{0}", param.Name);
                    }
                    // 直接传对象
                    //builder.Append("data, callback");
                    builder.AppendLine(")");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("var obj = {};");
                        builder.AppendLine("obj.Protocol = {0};", agent.Protocol);
                        builder.AppendLine("obj.Stub = {0};", i);
                        builder.AppendLine("obj.JO = {};");
                        for (int j = 0, n = parameters.Length - 1; j <= n; j++)
                        {
                            var param = parameters[j];
                            if (hasAsync && param.ParameterType.IsDelegate())
                                continue;
                            builder.Append("if ({0}) ", param.Name);
                            builder.Append("obj.JO.{0} = ", param.Name);
                            if (param.ParameterType == typeof(DateTime))
                                //builder.Append("JSON.stringify({0}).replace(\"T\", \" \").replace(\"Z\", \"\")", param.Name);
                                builder.Append("{0}", param.Name);
                            //else if (param.ParameterType.IsCustomType())
                            //    builder.Append("JSON.stringify({0})", param.Name);
                            else
                                builder.Append("{0}", param.Name);
                            builder.AppendLine(";");
                        }
                        builder.AppendLine("obj.JO = JSON.stringify(obj.JO);");
                        builder.AppendLine("{0}.ws.send(JSON.stringify(obj));", name, agent.Protocol, method.Name);
                    });
                }
            }

            private void WriteCallbackObject(StringBuilder builder, MethodInfo[] callback)
            {
                foreach (var method in callback)
                {
                    builder.AppendLine("public class RET_{0}_{1} : ProtocolPack", type.Name, method.Name);
                    builder.AppendBlock(() =>
                    {
                        var parameters = method.GetParameters();
                        foreach (var param in parameters)
                        {
                            builder.AppendLine("public {0} {1};", param.ParameterType.CodeName(), param.Name);
                        }
                    });
                }
            }
        }


        public static void BuildProtocolAgentBinary(string outputClientPath, string outputServerPath, string dllOrWithType, bool isUseJson)
        {
            Action<Type> build = (type) =>
            {
                ProtocolDefault writer = new ProtocolDefault();
                if (isUseJson)
                    writer.IsUseJson = true;
                writer.Write(type, outputClientPath, outputServerPath);
            };

            // DLL文件将生成所有带
            if (File.Exists(dllOrWithType))
            {
                var assembly = Assembly.LoadFile(dllOrWithType);
                foreach (var type in assembly.GetTypesWithAttribute<ProtocolStubAttribute>())
                    build(type);
            }
            else
            {
                Type type = GetDllType(dllOrWithType);
                if (type == null)
                    throw new ArgumentNullException("type");
                build(type);
            }
        }
        public static void BuildProtocolAgentHttp(string outputClientPath, string outputServerPath, string dllOrWithType, int csharp0js1ws2)
        {
            ProtocolDefault writer;
            switch (csharp0js1ws2)
            {
                // C#
                case 0:
                    writer = new ProtocolHttp(); break;

                // JS
                case 1:
                case 3:
                    writer = new ProtocolHttpJS(); break;

                // WebSocket JS
                case 2:
                case 4:
                    writer = new ProtocolJsonJs(); break;

                default: throw new NotImplementedException();
            }
            // DLL文件将生成所有带ProtocolStubAttribute类型的接口
            if (File.Exists(dllOrWithType))
            {
                var assembly = Assembly.LoadFile(dllOrWithType);
                foreach (var type in assembly.GetTypesWithAttribute<ProtocolStubAttribute>())
                {
                    writer.Write(type, outputClientPath, outputServerPath);
                    writer.Save();
                }
            }
            else
            {
                Type type = GetDllType(dllOrWithType);
                if (type == null)
                    throw new ArgumentNullException("type");
                writer.Write(type, outputClientPath, outputServerPath);
                writer.Save();
            }

            writer.Over();
        }
        /// <param name="isStatic">非静态的使用场景：需要连接多个数据结构完全一样的数据库时，例如GM工具为每一个需要操作的数据库构建一个操作实例</param>
        public static void BuildDatabaseMysql(string dll, string nsOrEmptyDotDBnameOrEmpty, string outputCS, string sharpIfBuild, string mysqlClassOrEmpty, bool isStatic)
        {
            /*
             * 优化
             * 1. 大数据表添加列时，在MySQL5.6将不重建表结构
             * 2. 主键在移除后，没有任何改变的CHANGE COLUMN也将影响所有数据，速度较慢
             * 3. 移除索引时，将影响所有数据，当数据量很大时，速度极慢
             * 4. 添加索引时，虽然影响行数为0，但当数据量很大时，任然很慢
             */
            Func<FieldInfo, bool> FieldCanUpdate = (f) =>
            {
                var attribute = f.GetAttribute<IndexAttribute>();
                return attribute == null || (attribute.Index != EIndex.Primary && attribute.Index != EIndex.Identity);
            };

            Assembly assembly = Assembly.LoadFrom(Path.GetFullPath(dll));
            var types = assembly.GetTypesWithAttribute<MemoryTableAttribute>(false).ToArray();

            // 外键：tree的第一层为主键表，主键表下有所有引用它的外键表，外键表可能有主键表
            TreeField tree = new TreeField();
            // 自增列
            Dictionary<Type, List<FieldInfo>> identities = new Dictionary<Type, List<FieldInfo>>();
            foreach (var table in types)
            {
                Console.WriteLine("dbtable: {0}", table.Name);
                var fields = table.GetFields();
                foreach (var field in fields)
                {
                    // 外键
                    var foreign = field.GetAttribute<ForeignAttribute>();
                    if (foreign != null)
                    {
                        var foreignKey = string.IsNullOrEmpty(foreign.ForeignField) ? field.Name : foreign.ForeignField;
                        var parentField = foreign.ForeignTable.GetField(foreignKey);
                        if (parentField == null)
                            throw new ArgumentNullException(string.Format("{0}.{1} required foreign key {2}.{3} is not exists.", table.Name, field.Name, foreign.ForeignTable.Name, foreignKey));
                        // 外键是内联表自身的某字段时，ForeignTable改为继承类型
                        if (foreign.ForeignTable == parentField.DeclaringType
                            && foreign.ForeignTable == field.DeclaringType
                            && foreign.ForeignTable.IsAssignableFrom(table))
                            parentField = table.GetField(foreignKey);
                        var parentIndex = parentField.GetAttribute<IndexAttribute>();
                        if (parentIndex == null || (parentIndex.Index != EIndex.Identity && parentIndex.Index != EIndex.Primary))
                            throw new NotSupportedException(string.Format("Table[{0}] is refferenced foreign field[{1}] must be a unique key.", parentField.ReflectedType.Name, foreignKey));
                        //var myIndex = field.GetAttribute<IndexAttribute>();
                        //if (myIndex == null)
                        //    throw new NotSupportedException(string.Format("Foreign key {0}.{1} must have a IndexAttribute.", table.Name, field.Name));
                        var parent = tree.Find(f => f.Field == parentField);
                        if (parent == null)
                        {
                            parent = new TreeField(parentField);
                            tree.Add(parent);
                        }
                        else if (parent.Parent != null && parent.Parent.Field == field)
                            throw new NotSupportedException("Foreign key can't be circular.");

                        // added as other foreign parent
                        var me = tree.Find(f => f.Field == field);
                        if (me == null)
                            me = new TreeField(field);

                        parent.Add(me);
                    }

                    // 自增键
                    var index = field.GetAttribute<IndexAttribute>();
                    if (index != null && index.Index == EIndex.Identity)
                    {
                        List<FieldInfo> list;
                        if (!identities.TryGetValue(table, out list))
                        {
                            list = new List<FieldInfo>();
                            identities[table] = list;
                        }
                        list.Add(field);
                    }
                }
            }

            StringBuilder builder = new StringBuilder();
            builder.AppendLine("using System;");
            builder.AppendLine("using System.Data;");
            builder.AppendLine("using System.Collections;");
            builder.AppendLine("using System.Collections.Generic;");
            builder.AppendLine("using System.Linq;");
            builder.AppendLine("using System.Reflection;");
            builder.AppendLine("using System.Text;");
            builder.AppendLine("using EntryEngine;");
            builder.AppendLine("using EntryEngine.Serialize;");
            builder.AppendLine("using EntryEngine.Network;");
            foreach (var ns in types.Select(t => t.Namespace).Distinct())
                if (!string.IsNullOrEmpty(ns))
                    builder.AppendLine("using {0};", ns);
            builder.AppendLine();

            if (string.IsNullOrEmpty(nsOrEmptyDotDBnameOrEmpty))
            {
                // 默认文件名作为类型名字
                nsOrEmptyDotDBnameOrEmpty = Path.GetFileNameWithoutExtension(outputCS);
            }
            else
            {
                int dot = nsOrEmptyDotDBnameOrEmpty.LastIndexOf('.');
                if (dot == -1)
                {
                    // 空命名空间
                    //builder.AppendLine("namespace {0}", nsOrEmptyDotDBnameOrEmpty);
                }
                else
                {
                    builder.AppendLine("namespace {0}", nsOrEmptyDotDBnameOrEmpty.Substring(0, dot));
                    nsOrEmptyDotDBnameOrEmpty = nsOrEmptyDotDBnameOrEmpty.Substring(dot + 1);
                }
            }
            builder.AppendLine("{");

            BindingFlags flag = BindingFlags.Public | BindingFlags.Instance;
            // build enum of table fields
            foreach (var table in types)
            {
                builder.AppendLine("public enum E{0}", table.Name);
                builder.AppendBlock(() =>
                {
                    var fields = table.GetFields(flag).Where(f => !f.HasAttribute<IgnoreAttribute>());
                    foreach (var field in fields)
                    {
                        builder.AppendSummary(field);
                        builder.AppendLine("{0},", field.Name);
                    }
                });
            }

            // 外键生成打平类型，后面生成连接查询的代码
            //tree.ForeachParentPriority(
            //    field => field.ChildCount == 0,
            //    field =>
            //    {
            //        if (field == tree)
            //            return;
            //        builder.AppendLine("public class JOIN_{0}", field.Table.Name);
            //        builder.AppendBlock(() =>
            //        {
            //            TreeField.ForParentPriority(field, null,
            //                foreign =>
            //                {
            //                    builder.AppendLine("public {0} {1};", foreign.Table.Name, foreign.Field.Name);
            //                });
            //        });
            //    });

            #region build mysql structure
            if (string.IsNullOrEmpty(mysqlClassOrEmpty))
            {
                mysqlClassOrEmpty = "MYSQL_DATABASE";
                builder.AppendLine("public class {0} : _DATABASE.Database", mysqlClassOrEmpty);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("protected override System.Data.IDbConnection CreateConnection()");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("var conn = new MySql.Data.MySqlClient.MySqlConnection();");
                        builder.AppendLine("conn.ConnectionString = ConnectionString;");
                        builder.AppendLine("conn.Open();");
                        builder.AppendLine("return conn;");
                    });
                });
            }
            builder.AppendLine("public class MYSQL_TABLE_COLUMN");
            builder.AppendBlock(() =>
            {
                builder.AppendLine("public string COLUMN_NAME;");
                builder.AppendLine("public string COLUMN_KEY;");
                builder.AppendLine("public string EXTRA;");
                builder.AppendLine("public string DATA_TYPE;");
                builder.AppendLine("public bool IsPrimary { get { return COLUMN_KEY == \"PRI\"; } }");
                builder.AppendLine("public bool IsIndex { get { return COLUMN_KEY == \"MUL\"; } }");
                builder.AppendLine("public bool IsUnique { get { return COLUMN_KEY == \"UNI\"; } }");
                builder.AppendLine("public bool IsIdentity { get { return EXTRA == \"auto_increment\"; } }");
                builder.AppendLine("public static HashSet<string> StringTypes = new HashSet<string>()");
                builder.AppendBlockWithEnd(() =>
                {
                    builder.AppendLine("\"char\",");
                    builder.AppendLine("\"varchar\",");
                    builder.AppendLine("\"tinytext\",");
                    builder.AppendLine("\"text\",");
                    builder.AppendLine("\"mediumtext\",");
                    builder.AppendLine("\"longtext\",");
                });
            });

            builder.AppendLine("public class MASTER_STATUS");
            builder.AppendBlock(() =>
            {
                builder.AppendLine("public string File;");
                builder.AppendLine("public int Position;");
                builder.AppendLine("public string Binlog_Do_DB;");
            });
            builder.AppendLine("public class SLAVE_STATUS");
            builder.AppendBlock(() =>
            {
                builder.AppendLine("public string Master_Host;");
                builder.AppendLine("public string Master_User;");
                builder.AppendLine("public int Master_Port;");
                builder.AppendLine("public string Master_Log_File;");
                builder.AppendLine("public int Read_Master_Log_Pos;");
                builder.AppendLine("public string Slave_IO_Running;");
                builder.AppendLine("public string Slave_SQL_Running;");
                builder.AppendLine("public string Replicate_Do_DB;");
                builder.AppendLine("public string Last_Error;");
                builder.AppendLine("public int Exec_Master_Log_Pos;");
                builder.AppendLine("public int Master_Server_Id;");
                builder.AppendLine("public bool IsRunning { get { return Slave_IO_Running == \"Yes\" && Slave_SQL_Running == \"Yes\"; } }");
                builder.AppendLine("public bool IsSynchronous { get { return Read_Master_Log_Pos == Exec_Master_Log_Pos; } }");
            });
            #endregion

            string _static = isStatic ? "static " : string.Empty;
            // build database operation
            builder.AppendLine("public {1}partial class {0}", nsOrEmptyDotDBnameOrEmpty, _static);
            builder.AppendBlock(() =>
            {
                // build mysql structure
                //builder.AppendLine("class MYSQL_TABLE_COLUMN");
                //builder.AppendBlock(() =>
                //{
                //    builder.AppendLine("public string COLUMN_NAME;");
                //    //builder.AppendLine("public string COLUMN_TYPE;");
                //    builder.AppendLine("public string COLUMN_KEY;");
                //    builder.AppendLine("public string EXTRA;");

                //    builder.AppendLine("public bool IsPrimary { get { return COLUMN_KEY == \"PRI\"; } }");
                //    builder.AppendLine("public bool IsIndex { get { return COLUMN_KEY == \"MUL\"; } }");
                //    builder.AppendLine("public bool IsUnique { get { return COLUMN_KEY == \"UNI\"; } }");
                //    builder.AppendLine("public bool IsIdentity { get { return EXTRA == \"auto_increment\"; } }");
                //});

                // database instance
                builder.AppendLine("public {0}bool IsDropColumn;", _static);
                builder.AppendLine("public {0}string DatabaseName;", _static);
                builder.AppendLine("public {0}Action<_DATABASE.Database> OnConstructDatabase;", _static);
                builder.AppendLine("public static List<MergeTable> AllMergeTable = new List<MergeTable>()");
                builder.AppendLine("{");
                foreach (var item in types)
                    builder.AppendLine("new MergeTable(\"{0}\"),", item.Name);
                builder.AppendLine("};");
                builder.AppendLine("private {0}_DATABASE.Database _dao;", _static);
                builder.AppendSummary("Set this will set the event 'OnCreateConnection' and 'OnTestConnection'");
                builder.AppendLine("public {0}_DATABASE.Database _DAO", _static);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("get { if (_dao == null) return _DATABASE._Database; else return _dao; }");
                    builder.AppendLine("set");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("if (_dao == value) return;");
                        //builder.AppendLine("if (_dao != null)");
                        //builder.AppendBlock(() =>
                        //{
                        //    builder.AppendLine("value.OnCreateConnection = null;");
                        //    builder.AppendLine("value.OnTestConnection = null;");
                        //});
                        builder.AppendLine("_dao = value;");
                        builder.AppendLine("if (value != null)");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("value.OnCreateConnection = CREATE_CONNECTION;");
                            builder.AppendLine("value.OnTestConnection = UPDATE_DATABASE_STRUCTURE;");
                        });
                    });
                });

                // create database
                builder.AppendSummary("Set this to the _DATABASE.Database.OnCreateConnection event");
                builder.AppendLine("public {0}void CREATE_CONNECTION(System.Data.IDbConnection conn, _DATABASE.Database database)", _static);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("if (string.IsNullOrEmpty(conn.Database) && !string.IsNullOrEmpty(DatabaseName) && !_DAO.Available)");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("var cmd = conn.CreateCommand();");
                        builder.AppendLine("cmd.CommandText = string.Format(\"CREATE DATABASE IF NOT EXISTS `{0}`;\", DatabaseName);");
                        builder.AppendLine("int create = cmd.ExecuteNonQuery();");
                        builder.AppendLine("conn.ChangeDatabase(DatabaseName);");
                        builder.AppendLine("_DAO.ConnectionString += string.Format(\"Database={0};\", DatabaseName);");
                        builder.AppendLine("_DAO.OnCreateConnection -= CREATE_CONNECTION;");
                        builder.AppendLine("if (create > 0)");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("_LOG.Info(\"Create database[`{0}`].\", DatabaseName);");
                        });
                        builder.AppendLine("_LOG.Info(\"Set database[`{0}`].\", DatabaseName);");
                    });
                });
                // update database structure
                builder.AppendSummary("Set this to the _DATABASE.Database.OnTestConnection event");
                builder.AppendLine("public {0}void UPDATE_DATABASE_STRUCTURE(System.Data.IDbConnection conn, _DATABASE.Database database)", _static);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("var cmd = conn.CreateCommand();");
                    builder.AppendLine("cmd.CommandTimeout = database.Timeout;");
                    builder.AppendLine("cmd.CommandText = string.Format(\"SELECT EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '{0}');\", conn.Database);");
                    builder.AppendLine("bool __exists = Convert.ToBoolean(cmd.ExecuteScalar());");

                    #region 创建数据表结构

                    builder.AppendLine("#region Create table");
                    builder.AppendLine("cmd.CommandText =");
                    builder.AppendLine("@\"");
                    // 创建数据表
                    foreach (var table in types)
                    {
                        builder.AppendLine("CREATE TABLE IF NOT EXISTS `{0}`", table.Name);
                        builder.AppendLine("(");
                        // fields
                        var fields = table.GetFields(flag).Where(f => !f.HasAttribute<IgnoreAttribute>());
                        var primary = fields.Where(f =>
                        {
                            var index = f.GetAttribute<IndexAttribute>();
                            return index != null && index.Index == EIndex.Primary;
                        }).ToArray();

                        if (primary.Length > 1)
                        {
                            var p2 = primary.Where(p => p.GetAttribute<ForeignAttribute>() == null);
                            // NotSupportedException: multiple primary key can't be refferenced by foreign key
                            if (tree.Find(f => p2.Contains(f.Field)) != null)
                                throw new NotSupportedException("Multiple primary key can't be refferenced by foreign key.");
                        }

                        fields.ForeachExceptLast(
                        (field) =>
                        {
                            builder.Append("`{0}` {1}", field.Name, GetMySqlType(field.FieldType, field));
                            IndexAttribute index = field.GetAttribute<IndexAttribute>();
                            if (index != null && index.Index == EIndex.Identity)
                            {
                                builder.Append(" PRIMARY KEY AUTO_INCREMENT");
                                if (primary.Length > 0)
                                    throw new NotSupportedException(string.Format("Table[{0}] can't has other primary key where table column has AUTO_INCREMENT as a primary key.", table.Name));
                            }
                        },
                        (field) =>
                        {
                            builder.AppendLine(",");
                        });
                        builder.AppendLine();
                        builder.AppendLine(");");
                    }
                    builder.AppendLine("\";");
                    builder.AppendLine("_LOG.Info(\"Begin create table.\");");
                    builder.AppendLine("cmd.ExecuteNonQuery();");
                    builder.AppendLine("_LOG.Info(\"Create table completed.\");");
                    builder.AppendLine("#endregion");

                    #endregion

                    #region 修改表结构

                    builder.AppendLine();
                    builder.AppendLine("Dictionary<string, MYSQL_TABLE_COLUMN> __columns = new Dictionary<string, MYSQL_TABLE_COLUMN>();");
                    builder.AppendLine("MYSQL_TABLE_COLUMN __value;");
                    builder.AppendLine("bool __noneChangePrimary;");
                    builder.AppendLine("bool __hasPrimary;");
                    builder.AppendLine("IDataReader reader;");
                    builder.AppendLine("StringBuilder builder = new StringBuilder();");

                    foreach (var table in types)
                    {
                        builder.AppendLine();
                        builder.AppendLine("#region Table structure \"{0}\"", table.Name);
                        builder.AppendLine("_LOG.Info(\"Begin update table[`{0}`] structure.\");", table.Name);
                        builder.AppendLine("__columns.Clear();");
                        builder.AppendLine("builder.Remove(0, builder.Length);");
                        //builder.AppendLine("builder = new StringBuilder();");
                        //builder.AppendLine("cmd = conn.CreateCommand();");
                        builder.AppendLine("cmd.CommandText = \"SELECT COLUMN_NAME, COLUMN_KEY, EXTRA, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME = '{0}' AND TABLE_SCHEMA = '\" + conn.Database + \"';\";", table.Name, nsOrEmptyDotDBnameOrEmpty);
                        builder.AppendLine("reader = cmd.ExecuteReader();");
                        builder.AppendLine("__hasPrimary = false;");
                        builder.AppendLine("foreach (var __column in _DATABASE.ReadMultiple<MYSQL_TABLE_COLUMN>(reader))");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("if (__column.IsPrimary) __hasPrimary = true;");
                            builder.AppendLine("__columns.Add(__column.COLUMN_NAME, __column);");
                        });
                        builder.AppendLine("reader.Close();");

                        // 不支持：字段改名；
                        // 支持：增删字段，修改字段类型；增删索引
                        var fields = table.GetFields(flag).Where(f => !f.HasAttribute<IgnoreAttribute>()).ToArray();
                        var primary = fields.Where(f =>
                        {
                            var index = f.GetAttribute<IndexAttribute>();
                            return index != null && (index.Index == EIndex.Primary || index.Index == EIndex.Identity);
                        }).ToArray();

                        // 更换主键
                        builder.AppendLine("__noneChangePrimary = true;");
                        for (int i = 0; i < primary.Length; i++)
                            builder.AppendLine("__noneChangePrimary &= (__columns.TryGetValue(\"{0}\", out __value) && __value.IsPrimary);", primary[i].Name);
                        builder.AppendLine("if (!__noneChangePrimary && __hasPrimary)");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("var pk = __columns.Values.FirstOrDefault(f => f.IsPrimary);");
                            builder.AppendLine("if (pk.IsIdentity) builder.AppendLine(\"ALTER TABLE `{0}` CHANGE COLUMN `{{0}}` `{{0}}` {{1}};\", pk.COLUMN_NAME, pk.DATA_TYPE);", table.Name);
                            builder.AppendLine("builder.AppendLine(\"ALTER TABLE `{0}` DROP PRIMARY KEY;\");", table.Name);
                            builder.AppendLine("_LOG.Debug(\"Drop primary key.\");");
                        });

                        // 删除旧索引
                        //builder.AppendLine("__hasPrimary = false;");
                        //builder.AppendLine("foreach (var __column in __columns.Values)");
                        //builder.AppendBlock(() =>
                        //{
                        //    builder.AppendLine("if (!__hasPrimary && __column.IsPrimary)");
                        //    builder.AppendBlock(() =>
                        //    {
                        //        builder.AppendLine("__hasPrimary = true;");
                        //        builder.AppendLine("if (!__column.IsIdentity)");
                        //        builder.AppendBlock(() =>
                        //        {
                        //            builder.AppendLine("builder.AppendLine(\"ALTER TABLE `{0}` DROP PRIMARY KEY;\");", table.Name);
                        //            builder.AppendLine("_LOG.Debug(\"Drop primary key.\");");
                        //        });
                        //    });
                        //    builder.AppendLine("if (__column.IsIndex || __column.IsUnique)");
                        //    builder.AppendBlock(() =>
                        //    {
                        //        builder.AppendLine("builder.AppendLine(\"ALTER TABLE {0} DROP INDEX `{{0}}`;\", __column.COLUMN_NAME);", table.Name);
                        //        builder.AppendLine("_LOG.Debug(\"Drop index[`{0}`].\", __column.COLUMN_NAME);");
                        //    });
                        //});
                        //builder.AppendLine();

                        //builder.AppendLine();
                        // 添加修改字段
                        for (int i = 0; i < fields.Length; i++)
                        {
                            StringBuilder temp = new StringBuilder();
                            var field = fields[i];
                            // 修改表顺序将重构表结构，跟增加删除表字段一样，需要较多时间
                            //temp.Append("builder.AppendLine(\"ALTER TABLE `{1}` {{0}} `{0}` {2}{{1}} {3}", field.Name, table.Name, GetMySqlType(field.FieldType), (i == 0 ? "FIRST" : "AFTER `" + fields[i - 1].Name + "`"));
                            temp.Append("builder.AppendLine(\"ALTER TABLE `{1}` {{0}} `{0}` {2}{{1}}", field.Name, table.Name, GetMySqlType(field.FieldType, field));
                            IndexAttribute index = field.GetAttribute<IndexAttribute>();
                            if (index != null && index.Index == EIndex.Identity)
                                temp.Append(" AUTO_INCREMENT");
                            temp.Append(";\");");
                            string result = temp.ToString();

                            builder.AppendLine("if (__columns.TryGetValue(\"{0}\", out __value))", field.Name);
                            builder.AppendBlock(() =>
                            {
                                bool isHasIndex = index != null && (index.Index == EIndex.Index || index.Index == EIndex.Group);
                                // 将字段类型从int改为字符串且之前是索引列时，由于索引没有key(10)，CHANGE COLUMN就会失败
                                if (isHasIndex && GetMySqlType(field.FieldType, null) == "VARCHAR(32)")
                                {
                                    builder.AppendLine("if (!MYSQL_TABLE_COLUMN.StringTypes.Contains(__value.DATA_TYPE) && (__value.IsIndex || __value.IsUnique))");
                                    builder.AppendBlock(() =>
                                    {
                                        // 将列置为非索引
                                        builder.AppendLine("__value.COLUMN_KEY = null;");
                                        builder.AppendLine("builder.AppendLine(\"ALTER TABLE {0} DROP INDEX `{{0}}`;\", __value.COLUMN_NAME);", table.Name);
                                        builder.AppendLine("_LOG.Debug(\"Drop index[`{0}`].\", __value.COLUMN_NAME);");
                                    });
                                }
                                builder.AppendLine(result, "CHANGE COLUMN `" + field.Name + "`",
                                    (index != null && index.Index == EIndex.Identity ? "\" + (__value.IsPrimary ? \"\" : \" PRIMARY KEY\") + \"" : ""));

                                if (isHasIndex)
                                {
                                    // ADD_INDEX: 当前是索引，以前不是索引时添加索引
                                    builder.AppendLine("if (!__value.IsIndex && !__value.IsUnique)");
                                    builder.AppendBlock(() =>
                                    {
                                        builder.AppendLine("builder.AppendLine(\"ALTER TABLE `{0}` ADD INDEX(`{1}`{2});\");", table.Name, field.Name, field.FieldType == typeof(string) ? "(10)" : string.Empty);
                                        builder.AppendLine("_LOG.Debug(\"Add index[`{0}`].\", __value.COLUMN_NAME);");
                                    });
                                }
                                else
                                {
                                    // 当前不是索引，以前是索引时删除索引
                                    builder.AppendLine("if (__value.IsIndex || __value.IsUnique)");
                                    builder.AppendBlock(() =>
                                    {
                                        builder.AppendLine("builder.AppendLine(\"ALTER TABLE {0} DROP INDEX `{{0}}`;\", __value.COLUMN_NAME);", table.Name);
                                        builder.AppendLine("_LOG.Debug(\"Drop index[`{0}`].\", __value.COLUMN_NAME);");
                                    });
                                }
                                builder.AppendLine("__columns.Remove(__value.COLUMN_NAME);");
                            });
                            builder.AppendLine("else");
                            builder.AppendBlock(() =>
                            {
                                builder.AppendLine(result, "ADD COLUMN", (index != null && index.Index == EIndex.Identity ? " PRIMARY KEY" : ""));
                                if (index != null && (index.Index == EIndex.Index || index.Index == EIndex.Group))
                                {
                                    // ADD_INDEX: 添加索引
                                    builder.AppendLine("builder.AppendLine(\"ALTER TABLE `{0}` ADD INDEX(`{1}`{2});\");", table.Name, field.Name, field.FieldType == typeof(string) ? "(10)" : string.Empty);
                                    builder.AppendLine("_LOG.Debug(\"Add index[`{{0}}`].\", \"{0}\");", field.Name);
                                }
                                builder.AppendLine("_LOG.Debug(\"Add column[`{{0}}`].\", \"{0}\");", field.Name);
                            });
                        }

                        // 防止新建服务时，服务的设置数据库设置成以前其它服务的数据库，导致删除了其它服务的数据
                        builder.AppendLine("if (IsDropColumn)");
                        builder.AppendBlock(() =>
                        {
                            // 删除字段
                            builder.AppendLine("foreach (var __column in __columns.Keys)");
                            builder.AppendBlock(() =>
                            {
                                // 数据库里会自动删除掉相应索引
                                builder.AppendLine("builder.AppendLine(\"ALTER TABLE `{0}` DROP COLUMN `{{0}}`;\", __column);", table.Name);
                                builder.AppendLine("_LOG.Debug(\"Drop column[`{0}`].\", __column);");
                            });
                        });

                        // 添加主键 & 索引
                        if (primary.Length > 0 && (primary.Length > 1 || primary[0].GetAttribute<IndexAttribute>().Index != EIndex.Identity))
                        {
                            builder.AppendLine("if (!__noneChangePrimary)");
                            builder.AppendBlock(() =>
                            {
                                builder.Append("builder.AppendLine(\"ALTER TABLE `{0}` ADD PRIMARY KEY (", table.Name);
                                primary.ForeachExceptLast(
                                    (field) => builder.Append("`{0}`{1}", field.Name,
                                        // 变长的类型建立索引需要指定长度 BLOB/TEXT column 'key' used in key specification without a key length
                                        // `key` TEXT, PRIMARY KEY (`key`(10))
                                        field.FieldType == typeof(string) ? "(10)" : string.Empty),
                                    (field) => builder.Append(","));
                                builder.AppendLine(");\");");
                                builder.AppendLine("_LOG.Debug(\"Add primary key[{{0}}].\", \"{0}\");", string.Join(",", primary.Select(p => p.Name).ToArray()));
                            });
                        }
                        //foreach (var field in fields)
                        //{
                        //    IndexAttribute index = field.GetAttribute<IndexAttribute>();
                        //    if (index != null && (index.Index == EIndex.Index || index.Index == EIndex.Group))
                        //    {
                        //        builder.AppendLine("builder.AppendLine(\"ALTER TABLE `{0}` ADD INDEX(`{1}`{2});\");", table.Name, field.Name, field.FieldType == typeof(string) ? "(10)" : string.Empty);
                        //        builder.AppendLine("_LOG.Debug(\"Add index[{0}].\");", field.Name);
                        //    }
                        //}

                        builder.AppendLine();
                        builder.AppendLine("cmd.CommandText = builder.ToString();");
                        builder.AppendLine("_LOG.Info(\"Building table[`{0}`] structure.\");", table.Name);
                        builder.AppendLine("cmd.ExecuteNonQuery();");
                        builder.AppendLine("#endregion");
                    }

                    #endregion

                    builder.AppendLine("if (!__exists) ");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("_LOG.Info(\"The first time to construct database.\");");
                        builder.AppendLine("if (OnConstructDatabase != null) OnConstructDatabase(database);");
                    });
                });

                #region 合并数据库
                builder.AppendSummary(string.Format("/* Phase说明 */ BuildTemp: 原库，可用于延长Timeout，{0}操作原库 / ChangeTemp: 临时库，可用于修改主键可能重复的数据，{0}操作临时库 / Merge: 临时库，可用于修改需要参考其它合服数据的数据，{0}操作目标库", nsOrEmptyDotDBnameOrEmpty));
                builder.AppendLine("public {0}void MERGE(MergeDatabase[] dbs, Action<_DATABASE.Database> phaseBuildTemp, Action<_DATABASE.Database> phaseChangeTemp, Action<_DATABASE.Database[]> phaseMerge)", _static);
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("_DATABASE.Database __target = _DAO;");
                    builder.AppendLine("if (__target == null) throw new ArgumentNullException(\"_DAO\");");
                    builder.AppendLine("if (__target.Available) throw new InvalidOperationException(\"_DAO can't be available.\");");
                    builder.AppendLine("_DATABASE.Database[] sources = new _DATABASE.Database[dbs.Length];");

                    // 自动合并自增列用
                    foreach (var item in identities)
                        foreach (var field in item.Value)
                            builder.AppendLine("{0} __{1}_{2} = 0;", field.FieldType.CodeName(), item.Key.Name, field.Name);

                    builder.AppendLine();
                    builder.AppendLine("#region create temp database");
                    #region 创建临时数据库

                    builder.AppendLine("for (int i = 0; i < sources.Length; i++)");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("_DATABASE.Database db = new ConnectionPool() {{ Base = new {0}() }};", mysqlClassOrEmpty);
                        builder.AppendLine("db.ConnectionString = dbs[i].ConnectionStringWithDB;");
                        // 创建数据库 & 数据表
                        builder.AppendLine("db.OnTestConnection = (__conn, __db) =>");
                        builder.AppendBlockWithEnd(() =>
                        {
                            builder.AppendLine("string __temp = \"TEMP_\" + db.DatabaseName;");
                            builder.AppendLine("db.ExecuteNonQuery(string.Format(\"DROP DATABASE IF EXISTS `{0}`; CREATE DATABASE `{0}`;\", __temp));");
                            builder.AppendLine("__conn.ChangeDatabase(__temp);");
                        });
                        builder.AppendLine("db.OnTestConnection += UPDATE_DATABASE_STRUCTURE;");
                        builder.AppendLine("db.OnTestConnection += (__conn, __db) => __conn.ChangeDatabase(db.DatabaseName);");
                        builder.AppendLine("db.TestConnection();");
                        builder.AppendLine("_DAO = db;");
                        builder.AppendLine("if (phaseBuildTemp != null) phaseBuildTemp(db);");
                        builder.AppendLine("sources[i] = db;");
                        builder.AppendLine("string dbName = db.DatabaseName;");
                        builder.AppendLine("string tempName = \"TEMP_\" + dbName;");
                        builder.AppendLine("_LOG.Info(\"Begin build temp database[{0}].\", dbName);");
                        builder.AppendLine();

                        // 若不指定数据表默认合并全部
                        builder.AppendLine("if (dbs[i].Tables == null) dbs[i].Tables = AllMergeTable.ToArray();");
                        builder.AppendLine();

                        // 筛选数据到临时数据库
                        builder.AppendLine("StringBuilder builder = new StringBuilder();");
                        builder.AppendLine("string result;");
                        builder.AppendLine("MergeTable table;");

                        Action<Type, Action, Action> _mergeTable = (type, a1, a2) =>
                        {
                            builder.AppendLine("table = dbs[i].Tables.FirstOrDefault(t => t.TableName == \"{0}\");", type.Name);
                            builder.AppendLine("if (table != null)");
                            builder.AppendBlock(() =>
                            {
                                if (a1 != null) a1();
                                builder.Append("builder.Append(\"INSERT INTO {0}.{1} SELECT ");
                                var fields = type.GetFields(flag).Where(f => !f.HasAttribute<IgnoreAttribute>()).ToArray();
                                for (int i = 0, n = fields.Length - 1; i <= n; i++)
                                {
                                    builder.Append("{{1}}.`{0}`", fields[i].Name);
                                    if (i != n)
                                        builder.Append(",");
                                }
                                builder.AppendLine(" FROM {2}.{1}\", tempName, table.TableName, dbName);");
                                builder.AppendLine("if (!string.IsNullOrEmpty(table.Where)) builder.Append(\" \" + table.Where);");
                                if (a2 != null) a2();
                                builder.AppendLine("builder.AppendLine(\";\");");
                                builder.AppendLine("result = builder.ToString();");
                                builder.AppendLine("builder.Remove(0, builder.Length);");
                                builder.AppendLine("_LOG.Info(\"Merge table[`{0}`] data.\", table.TableName);");
                                builder.AppendLine("_LOG.Debug(\"SQL: {0}\", result);");
                                builder.AppendLine("db.ExecuteNonQuery(result);");
                            });
                        };
                        // 主键表优先插入
                        foreach (var item in tree)
                        {
                            _mergeTable(item.Table, null, null);
                        }
                        // 插入其它表
                        foreach (var type in types)
                        {
                            // 跳过已经插入的主键表
                            if (tree.Any(t => t.Table == type))
                                continue;
                            var foreign = type.GetFields().Where(f => f.HasAttribute<ForeignAttribute>()).ToArray();
                            if (foreign.Length > 0)
                            {
                                // 外键表需要插入主键表剩余的数据
                                _mergeTable(type, () =>
                                {
                                    builder.AppendLine("bool __flag = false;");
                                },
                                () =>
                                {
                                    for (int i = 0; i < foreign.Length; i++)
                                    {
                                        var field = foreign[i];
                                        var att = field.GetAttribute<ForeignAttribute>();
                                        builder.AppendLine("if (dbs[i].Tables.Any(t => t.TableName == \"{0}\"))", att.ForeignTable.Name);
                                        builder.AppendBlock(() =>
                                        {
                                            builder.AppendLine("if (!__flag)");
                                            builder.AppendBlock(() =>
                                            {
                                                builder.AppendLine("__flag = true;");
                                                builder.AppendLine("builder.Append(\" WHERE EXISTS \");");
                                                // :INSERT2
                                                builder.AppendLine("builder.Append(\"(SELECT {{0}}.{0}.{1} FROM {{0}}.{0} WHERE {2}.{3} = {{0}}.{0}.{1})\", tempName);", att.ForeignTable.Name, string.IsNullOrEmpty(att.ForeignField) ? field.Name : att.ForeignField, type.Name, field.Name);
                                            });
                                            if (i != 0)
                                            {
                                                builder.AppendLine("else");
                                                builder.AppendBlock(() =>
                                                {
                                                    builder.AppendLine("builder.AppendLine(\" AND \");");
                                                    // :INSERT2
                                                    //builder.AppendLine("builder.Append(\"(SELECT __{0}.{1} FROM __{0} WHERE {2}.{3} = __{0}.{1})\");", att.ForeignTable.Name, string.IsNullOrEmpty(att.ForeignField) ? field.Name : att.ForeignField, type.Name, field.Name);
                                                    builder.AppendLine("builder.Append(\"(SELECT {{0}}.{0}.{1} FROM {{0}}.{0} WHERE {2}.{3} = {{0}}.{0}.{1})\", tempName);", att.ForeignTable.Name, string.IsNullOrEmpty(att.ForeignField) ? field.Name : att.ForeignField, type.Name, field.Name);
                                                });
                                            }
                                        });
                                    }
                                });
                            }
                            else
                                _mergeTable(type, null, null);
                        }

                        builder.AppendLine("_LOG.Info(\"Build temp database[{0}] completed.\", dbName);");
                        builder.AppendLine("db.OnCreateConnection = (conn, __db) => conn.ChangeDatabase(tempName);");
                        builder.AppendLine("// 对临时数据库的表自动修改自增列");
                        // 自动合并自增键
                        foreach (var item in identities)
                        {
                            builder.AppendLine("table = dbs[i].Tables.FirstOrDefault(t => t.TableName == \"{0}\");", item.Key.Name);
                            builder.AppendLine("if (table.AutoMergeIdentity)");
                            builder.AppendBlock(() =>
                            {
                                foreach (var field in item.Value)
                                {
                                    builder.AppendLine("UpdateIdentityKey_{0}_{1}(ref __{0}_{1});", item.Key.Name, field.Name);
                                    builder.AppendLine("_LOG.Info(\"自动修改自增列`{0}`.{1}\");", item.Key.Name, field.Name);
                                }
                            });
                        }
                        builder.AppendLine("if (phaseChangeTemp != null) phaseChangeTemp(db);");
                    });

                    #endregion
                    builder.AppendLine("#endregion");

                    builder.AppendLine();
                    builder.AppendLine("_LOG.Info(\"Build all temp database completed.\");");
                    builder.AppendLine("#region import data in temp database to merge target");
                    #region 将临时数据库数据导入目标库

                    builder.AppendLine();
                    // 修改可能重复的主键
                    builder.AppendLine("if (phaseMerge != null) phaseMerge(sources);");
                    // 新建目标数据库
                    builder.AppendLine("_DAO = __target;");
                    builder.AppendLine("_DAO.OnCreateConnection = (conn, __db) =>");
                    builder.AppendBlockWithEnd(() =>
                    {
                        builder.AppendLine("if (string.IsNullOrEmpty(conn.Database) && !string.IsNullOrEmpty(DatabaseName))");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("var cmd = conn.CreateCommand();");
                            builder.AppendLine("cmd.CommandText = string.Format(\"DROP DATABASE IF EXISTS `{0}`; CREATE DATABASE `{0}`;\", DatabaseName);");
                            builder.AppendLine("cmd.ExecuteNonQuery();");
                            builder.AppendLine("conn.ChangeDatabase(DatabaseName);");
                            builder.AppendLine("_DAO.ConnectionString += string.Format(\"Database={0};\", DatabaseName);");
                        });
                    });
                    builder.AppendLine("_DAO.TestConnection();");
                    builder.AppendLine("for (int i = 0; i < sources.Length; i++)");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("var db = sources[i];");
                        builder.AppendLine("var tables = dbs[i].Tables;");
                        builder.AppendLine("string tempName = \"TEMP_\" + db.DatabaseName;");
                        builder.AppendLine("_LOG.Info(\"Begin merge from temp database[`{0}`].\", db.DatabaseName);");
                        builder.AppendLine("if (db.DataSource == _DAO.DataSource)");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("for (int j = 0; j < tables.Length; j++)");
                            builder.AppendBlock(() =>
                            {
                                builder.AppendLine("_LOG.Debug(\"Merge table[`{0}`].\", tables[j].TableName);");
                                builder.AppendLine("_DAO.ExecuteNonQuery(string.Format(\"INSERT INTO {1} SELECT * FROM {0}.{1};\", tempName, tables[j].TableName));");
                            });
                        });
                        builder.AppendLine("else");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("StringBuilder builder = new StringBuilder();");
                            builder.AppendLine("MergeTable table;");
                            foreach (var type in types)
                            {
                                builder.AppendLine("table = dbs[i].Tables.FirstOrDefault(t => t.TableName == \"{0}\");", type.Name);
                                builder.AppendLine("if (table != null)");
                                builder.AppendBlock(() =>
                                {
                                    builder.AppendLine("_LOG.Debug(\"Merge table[`{0}`].\", table.TableName);");
                                    builder.AppendLine("var list = db.SelectObjects<{0}>(\"SELECT * FROM {0};\");", type.Name);
                                    builder.AppendLine("if (list.Count > 0)");
                                    builder.AppendBlock(() =>
                                    {
                                        builder.AppendLine("foreach (var item in list)");
                                        builder.AppendBlock(() =>
                                        {
                                            builder.AppendLine("_{0}.Insert(item);", type.Name);
                                        });
                                    });
                                });
                            };
                        });
                        builder.AppendLine("_LOG.Info(\"Merge database[`{0}`] completed!\", db.DatabaseName);");
                        builder.AppendLine("db.ExecuteNonQuery(\"DROP DATABASE \" + tempName);");
                        builder.AppendLine("db.Dispose();");
                    });

                    #endregion
                    builder.AppendLine("#endregion");
                });
                #endregion

                #region 修改键
                // 外键
                tree.ForeachParentPriority(
                    field => field.ChildCount == 0,
                    field =>
                    {
                        if (field == tree)
                            return;
                        // 删除
                        builder.AppendLine("public {3}int DeleteForeignKey_{0}_{1}({2} target)", field.Table.Name, field.Field.Name, field.Field.FieldType.CodeName(), _static);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("StringBuilder builder = new StringBuilder();");
                            TreeField.ForParentPriority(field, null,
                                foreign =>
                                {
                                    builder.AppendLine("builder.AppendLine(\"DELETE FROM `{0}` WHERE `{1}` = @p0;\");", foreign.Table.Name, foreign.Field.Name);
                                });
                            builder.AppendLine("return _DAO.ExecuteNonQuery(builder.ToString(), target);");
                        });
                        builder.AppendLine("public {3}void DeleteForeignKey_{0}_{1}({2} target, StringBuilder builder, List<object> args)", field.Table.Name, field.Field.Name, field.Field.FieldType.CodeName(), _static);
                        builder.AppendBlock(() =>
                        {
                            TreeField.ForParentPriority(field, null,
                                foreign =>
                                {
                                    builder.AppendLine("builder.AppendLine(\"DELETE FROM `{0}` WHERE `{1}` = @p{{0}};\", args.Count);", foreign.Table.Name, foreign.Field.Name);
                                });
                            builder.AppendLine("args.Add(target);");
                        });
                        // 修改
                        builder.AppendLine("public {3}int UpdateForeignKey_{0}_{1}({2} origin, {2} target)", field.Table.Name, field.Field.Name, field.Field.FieldType.CodeName(), _static);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("StringBuilder builder = new StringBuilder();");
                            TreeField.ForParentPriority(field, null,
                                foreign =>
                                {
                                    builder.AppendLine("builder.AppendLine(\"UPDATE `{0}` SET `{1}` = @p0 WHERE `{1}` = @p1;\");", foreign.Table.Name, foreign.Field.Name);
                                });
                            builder.AppendLine("return _DAO.ExecuteNonQuery(builder.ToString(), target, origin);");
                        });
                    });
                // 自增列
                foreach (var item in identities)
                {
                    foreach (var field in item.Value)
                    {
                        string keyType = field.FieldType.CodeName();
                        // 自增键
                        builder.AppendLine("public {3}void UpdateIdentityKey_{0}_{1}(ref {2} start)", item.Key.Name, field.Name, keyType, _static);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("{0} min = _DAO.ExecuteScalar<{0}>(\"SELECT MIN(`{1}`) FROM `{2}`;\");", keyType, field.Name, item.Key.Name);
                            builder.AppendLine("{0} max = _DAO.ExecuteScalar<{0}>(\"SELECT MAX(`{1}`) FROM `{2}`;\");", keyType, field.Name, item.Key.Name);
                            builder.AppendLine("if (start > 0)");
                            builder.AppendBlock(() =>
                            {
                                builder.AppendLine("if (min > start) min = start - min;");
                                builder.AppendLine("else min = Math.Max(start, max + 1) - min;");
                                builder.AppendLine("start = max + min + 1;");
                            });
                            builder.AppendLine("else");
                            builder.AppendBlock(() =>
                            {
                                builder.AppendLine("start = max + 1;");
                                builder.AppendLine("return;");
                            });
                            builder.AppendLine("StringBuilder builder = new StringBuilder();");
                            builder.AppendLine("builder.AppendLine(\"UPDATE `{0}` SET `{1}` = `{1}` + @p0;\");", item.Key.Name, field.Name);
                            // 外键引用自增列的也需要改
                            TreeField tf = tree.Find(t => t.Field == field);
                            if (tf != null)
                                foreach (var foreign in tf)
                                    builder.AppendLine("builder.AppendLine(\"UPDATE `{0}` SET `{1}` = `{1}` + @p0;\");", foreign.Table.Name, foreign.Field.Name);
                            builder.AppendLine("_DAO.ExecuteNonQuery(builder.ToString(), min);");
                        });
                    }
                }
                #endregion

                #region 针对每张表的其它操作

                // Join类型
                foreach (var table in types)
                {
                    var fields = table.GetFields(flag).Where(f => !f.HasAttribute<IgnoreAttribute>()).ToArray();
                    var foreignFields = fields.Where(f => f.HasAttribute<ForeignAttribute>()).ToArray();
                    if (foreignFields.Length > 0)
                    {
                        ForeignAttribute[] foreigns = new ForeignAttribute[foreignFields.Length];
                        for (int i = 0; i < foreigns.Length; i++)
                            foreigns[i] = foreignFields[i].GetAttribute<ForeignAttribute>();

                        // 生成外包类型
                        builder.AppendLine("public class Join{0}", table.Name);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("public {0} {0};", table.Name);
                            for (int i = 0; i < foreigns.Length; i++)
                            {
                                builder.AppendLine("public {0} {1};", foreigns[i].ForeignTable.Name, foreignFields[i].Name);
                            }
                        });
                    }
                }

                foreach (var table in types)
                {
                    if (!isStatic)
                        builder.AppendLine();
                    var fields = table.GetFields(flag).Where(f => !f.HasAttribute<IgnoreAttribute>()).ToArray();

                    string tableMapperName = "_" + table.Name;

                    #region 自动解析特殊类型

                    bool hasSpecial = false;
                    foreach (var field in fields)
                    {
                        bool special;
                        string type = GetMySqlType(field.FieldType, out special, null);
                        if (!special)
                            continue;
                        if (!hasSpecial)
                        {
                            if (!isStatic)
                            {
                                tableMapperName = "_" + tableMapperName;
                            }
                            // 创建类型
                            builder.AppendLine("public partial class {0} : {1}", tableMapperName, table.Name);
                            builder.AppendLine("{");
                        }
                        builder.AppendLine("public new string {0}", field.Name);
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("get {{ return JsonWriter.Serialize(base.{0}, typeof({1})); }}", field.Name, field.FieldType.CodeName());
                            builder.AppendLine("set {{ base.{0} = JsonReader.Deserialize<{1}>(value); }}", field.Name, field.FieldType.CodeName());
                        });
                        hasSpecial = true;
                    }

                    if (isStatic && !hasSpecial)
                    {
                        // 创建类型
                        builder.AppendLine("public partial class {0} : {1}", tableMapperName, table.Name);
                        builder.AppendLine("{");
                    }

                    if (hasSpecial)
                    {
                        builder.AppendLine();
                        builder.AppendLine("public {0}() {{ }}", tableMapperName);
                        builder.AppendLine("public {0}({1} __clone)", tableMapperName, table.Name);
                        builder.AppendBlock(() =>
                        {
                            foreach (var field in fields)
                                builder.AppendLine("base.{0} = __clone.{0};", field.Name);
                        });
                    }
                    else
                    {
                        //if (!isStatic)
                        tableMapperName = table.Name;
                    }

                    #endregion

                    #region 数据表类型操作

                    StringBuilder builderOP = new StringBuilder();
                    // 静态就将操作方法直接写入类型中，否则将生成新实例类型来存放操作方法
                    // 以保持静态和非静态的操作代码相同，在静态和非静态改动时可以比较方便
                    // 例如：静态_DB._TUser.Insert => 非静态db._TUser.Insert（仅需要替换静态类型=>实例）
                    string dbInstanceName = isStatic ? "" : string.Format("_{0}.", nsOrEmptyDotDBnameOrEmpty);
                    if (!isStatic)
                    {
                        // 创建实例类型来操作表
                        builderOP.AppendLine("private ___{0} __ins{0};", table.Name);
                        builderOP.AppendLine("public ___{0} _{0}", table.Name);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("get");
                            builderOP.AppendBlock(() =>
                            {
                                builderOP.AppendLine("if (__ins{0} == null) __ins{0} = new ___{0}(this);", table.Name);
                                builderOP.AppendLine("return __ins{0};", table.Name);
                            });
                        });
                        builderOP.AppendLine("public class ___{0}", table.Name);
                        builderOP.AppendLine("{");
                        builderOP.AppendLine("private {0} _{0};", nsOrEmptyDotDBnameOrEmpty);
                        builderOP.AppendLine("public _DATABASE.Database _DAO {{ get {{ return _{0}._DAO; }} }}", nsOrEmptyDotDBnameOrEmpty);
                        builderOP.AppendLine("internal ___{0}({1} ___db) {{ this._{1} = ___db; }}", table.Name, nsOrEmptyDotDBnameOrEmpty);
                    }

                    if (hasSpecial)
                    {
                        builderOP.AppendLine("public{2} {0} __TO({1} __base)", table.Name, tableMapperName, isStatic ? " static" : "");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("if (__base == null) return null;");
                            builderOP.AppendLine("{0} ___base = __base;", table.Name);
                            builderOP.AppendLine("{0} __copy = new {0}();", table.Name);
                            foreach (var field in fields)
                                builderOP.AppendLine("__copy.{0} = ___base.{0};", field.Name);
                            if (table.Is(typeof(IDBCopy<>).MakeGenericType(table)))
                                builderOP.AppendLine("((IDBCopy<{0}>)___base).DBCopyTo(__copy);", table.Name);
                            builderOP.AppendLine("return __copy;");
                        });
                    }

                    // 字段对应枚举
                    builderOP.AppendLine("public {2}E{0}[] FIELD_ALL = {{ {1} }};", table.Name,
                        string.Join(", ", fields.Select(f => string.Format("E{0}.{1}", table.Name, f.Name)).ToArray()), _static);
                    //builderOP.AppendLine("private static E{0}[] FIELD_UPDATE = {{ {1} }};", table.Name,
                    //    string.Join(", ", fields.Where(field => !field.HasAttribute<IndexAttribute>() && !field.HasAttribute<ForeignAttribute>()).Select(f => string.Format("E{0}.{1}", table.Name, f.Name)).ToArray()));
                    builderOP.AppendLine("public {2}E{0}[] FIELD_UPDATE = {{ {1} }};", table.Name,
                        string.Join(", ", fields.Where(FieldCanUpdate).Select(f => string.Format("E{0}.{1}", table.Name, f.Name)).ToArray()), _static);
                    builderOP.AppendLine("public {0}E{1}[] NoNeedField(params E{1}[] noNeed)", _static, table.Name);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("if (noNeed.Length == 0) return FIELD_ALL;");
                        builderOP.AppendLine("List<E{0}> list = new List<E{0}>(FIELD_ALL.Length);", table.Name);
                        builderOP.AppendLine("for (int i = 0; i < FIELD_ALL.Length; i++)");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("if (!noNeed.Contains(FIELD_ALL[i])) list.Add(FIELD_ALL[i]);");
                        });
                        builderOP.AppendLine("return list.ToArray();");
                    });
                    builderOP.AppendLine("public {0}int FieldCount {{ get {{ return FIELD_ALL.Length; }} }}", _static);
                    builderOP.AppendLine();

                    // 键
                    FieldInfo identity = null;
                    List<FieldInfo> primaryFields = new List<FieldInfo>();
                    List<FieldInfo> groupFields = new List<FieldInfo>();
                    foreach (var f in fields)
                    {
                        var index = f.GetAttribute<IndexAttribute>();
                        if (index != null)
                        {
                            if (index.Index == EIndex.Group)
                            {
                                groupFields.Add(f);
                            }
                            else if (index.Index == EIndex.Primary)
                            {
                                primaryFields.Add(f);
                            }
                            else if (index.Index == EIndex.Identity)
                            {
                                if (identity == null)
                                {
                                    identity = f;
                                }
                                else
                                {
                                    Console.WriteLine("{0}自增键有多个，设计可能不合理", table.Name);
                                }
                                primaryFields.Add(f);
                            }
                        }
                    }

                    // 读取方法
                    builderOP.AppendLine("public {1}{0} Read(IDataReader reader)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("return Read(reader, 0, FieldCount);");
                    });
                    builderOP.AppendLine("public {1}{0} Read(IDataReader reader, int offset)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("return Read(reader, offset, FieldCount);");
                    });
                    builderOP.AppendLine("public {1}{0} Read(IDataReader reader, int offset, int fieldCount)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.Append("return ");
                        if (hasSpecial)
                            builderOP.Append("__TO(");
                        builderOP.Append("_DATABASE.ReadObject<{0}>(reader, offset, fieldCount)", tableMapperName);
                        if (hasSpecial)
                            builderOP.Append(")");
                        builderOP.AppendLine(";");
                    });
                    builderOP.AppendLine("public {0}void MultiReadPrepare(IDataReader reader, int offset, int fieldCount, out List<PropertyInfo> properties, out List<FieldInfo> fields, ref int[] indices)", _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("_DATABASE.MultiReadPrepare(reader, typeof({0}), offset, fieldCount, out properties, out fields, ref indices);", tableMapperName);
                    });
                    builderOP.AppendLine("public {1}{0} MultiRead(IDataReader reader, int offset, int fieldCount, List<PropertyInfo> properties, List<FieldInfo> fields, int[] indices)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.Append("return ");
                        if (hasSpecial)
                            builderOP.Append("__TO(");
                        builderOP.Append("_DATABASE.MultiRead<{0}>(reader, offset, fieldCount, properties, fields, indices)", tableMapperName);
                        if (hasSpecial)
                            builderOP.Append(")");
                        builderOP.AppendLine(";");
                    });

                    // 增
                    builderOP.AppendLine("public {1}void GetInsertSQL({0} target, StringBuilder builder, List<object> values)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("int index = values.Count;");
                        string target = "target";
                        if (!string.IsNullOrEmpty(sharpIfBuild))
                            builderOP.AppendLine("#if !{0}", sharpIfBuild);
                        if (hasSpecial)
                        {
                            builderOP.AppendLine("{0} _{1} = new {0}({1});", tableMapperName, target);
                            target = "_" + target;
                        }
                        builderOP.Append("builder.AppendFormat(\"INSERT `{0}`(", table.Name);
                        StringBuilder temp1 = new StringBuilder();
                        int last = fields.Length - 1;
                        int offset = 0;
                        for (int i = 0; i <= last; i++)
                        {
                            var field = fields[i];

                            var index = field.GetAttribute<IndexAttribute>();
                            if (index != null && index.Index == EIndex.Identity)
                            {
                                offset++;
                                continue;
                            }

                            if (i != offset)
                                builderOP.Append(", ", field.Name);
                            builderOP.Append("`{0}`", field.Name);
                            temp1.AppendLine("values.Add({0}.{1});", target, field.Name);
                        }
                        if (identity != null)
                            temp1.AppendLine("builder.Append(\"SELECT LAST_INSERT_ID();\");");
                        builderOP.AppendLine(") VALUES(\");");
                        builderOP.AppendLine("for (int i = 0, n = {0}; i <= n; i++)", last - offset);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("builder.AppendFormat(\"@p{0}\", index++);");
                            builderOP.AppendLine("if (i != n) builder.Append(\", \");");
                        });
                        builderOP.AppendLine("builder.AppendLine(\");\");");
                        builderOP.Append(temp1.ToString());
                        if (!string.IsNullOrEmpty(sharpIfBuild))
                            builderOP.AppendLine("#endif");
                        //builderOP.AppendLine("return builder.ToString();");
                    });

                    builderOP.AppendLine("public {1}{2} Insert({0} target)", table.Name, _static, identity == null ? "void" : identity.FieldType.CodeName());
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("StringBuilder builder = new StringBuilder();");
                        builderOP.AppendLine("List<object> values = new List<object>({0});", fields.Length);
                        builderOP.AppendLine("GetInsertSQL(target, builder, values);");
                        if (identity != null)
                        {
                            builderOP.AppendLine("builder.Append(\"SELECT LAST_INSERT_ID();\");");
                            //builderOP.AppendLine("return _DAO.SelectValue<{0}>(builder.ToString(), values.ToArray());", identity.FieldType.CodeName());
                            builderOP.AppendLine("target.{1} = _DAO.SelectValue<{0}>(builder.ToString(), values.ToArray());", identity.FieldType.CodeName(), identity.Name);
                            builderOP.AppendLine("return target.{0};", identity.Name);
                        }
                        else
                        {
                            builderOP.AppendLine("_DAO.ExecuteNonQuery(builder.ToString(), values.ToArray());");
                        }
                    });

                    // 删
                    if (primaryFields.Count > 0)
                    {
                        // 删除语句
                        builderOP.Append("public {0}void GetDeleteSQL(", _static);
                        for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                        {
                            var field = primaryFields[i];
                            builderOP.Append("{0} {1}", field.FieldType.CodeName(), field.Name);
                            //if (i != n)
                            builderOP.Append(", ");
                        }
                        builderOP.AppendLine("StringBuilder builder, List<object> values)");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("int index = values.Count;");
                            builderOP.Append("builder.AppendFormat(\"DELETE FROM `{0}` WHERE ", table.Name);
                            for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                            {
                                var field = primaryFields[i];
                                builderOP.Append("`{0}` = @p{{{1}}}", field.Name, i);
                                if (i != n)
                                    builderOP.Append(" AND ");
                            }
                            builderOP.Append(";\"");
                            foreach (var field in primaryFields)
                            {
                                builderOP.Append(", index++", field.Name);
                            }
                            builderOP.AppendLine(");");
                            foreach (var field in primaryFields)
                            {
                                builderOP.AppendLine("values.Add({0});", field.Name);
                            }
                        });

                        // 删除执行
                        builderOP.Append("public {0}int Delete(", _static);
                        for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                        {
                            var field = primaryFields[i];
                            builderOP.Append("{0} {1}", field.FieldType.CodeName(), field.Name);
                            if (i != n)
                                builderOP.Append(", ");
                        }
                        builderOP.AppendLine(")");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.Append("return _DAO.ExecuteNonQuery(\"DELETE FROM `{0}` WHERE ", table.Name);
                            for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                            {
                                var field = primaryFields[i];
                                builderOP.Append("`{0}` = @p{1}", field.Name, i);
                                if (i != n)
                                    builderOP.Append(" AND ");
                            }
                            builderOP.Append("\", ");
                            for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                            {
                                var field = primaryFields[i];
                                builderOP.Append("{0}", field.Name);
                                if (i != n)
                                    builderOP.Append(", ");
                            }
                            builderOP.AppendLine(");");
                        });

                        // 单个主键可以作为Group删除
                        if (primaryFields.Count > 1)
                        {
                            foreach (var field in primaryFields)
                            {
                                builderOP.AppendLine("public {0}int DeleteBy{1}({2} {1})", _static, field.Name, field.FieldType.CodeName());
                                builderOP.AppendBlock(() =>
                                {
                                    builderOP.AppendLine("return _DAO.ExecuteNonQuery(\"DELETE FROM `{0}` WHERE `{1}` = @p0;\", {1});", table.Name, field.Name);
                                });
                            }
                        }
                    }
                    if (groupFields.Count > 0)
                    {
                        foreach (var field in groupFields)
                        {
                            builderOP.AppendLine("public {0}int DeleteBy{1}({2} {1})", _static, field.Name, field.FieldType.CodeName());
                            builderOP.AppendBlock(() =>
                            {
                                builderOP.AppendLine("return _DAO.ExecuteNonQuery(\"DELETE FROM `{0}` WHERE `{1}` = @p0;\", {1});", table.Name, field.Name);
                            });
                        }
                    }

                    // 改
                    var canUpdate = fields.Where(FieldCanUpdate).ToArray();
                    if (canUpdate.Length > 0)
                    {
                        builderOP.AppendLine("public {0}void GetUpdateSQL({1} target, string condition, StringBuilder builder, List<object> values, params E{1}[] fields)", _static, table.Name);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("int index = values.Count;");
                            string target = "target";
                            if (!string.IsNullOrEmpty(sharpIfBuild))
                                builderOP.AppendLine("#if !{0}", sharpIfBuild);
                            if (hasSpecial)
                            {
                                builderOP.AppendLine("{0} _{1} = new {0}({1});", tableMapperName, target);
                                target = "_" + target;
                            }
                            //builderOP.AppendLine("if (fields.Length == 0) fields = FIELD_UPDATE;");
                            builderOP.AppendLine("bool all = fields.Length == 0 || fields == FIELD_UPDATE;");
                            builderOP.AppendLine("builder.Append(\"UPDATE `{0}` SET\");", table.Name);

                            foreach (var field in canUpdate)
                            {
                                builderOP.AppendLine("if (all || fields.Contains(E{0}.{1}))", table.Name, field.Name);
                                builderOP.AppendBlock(() =>
                                {
                                    builderOP.AppendLine("builder.AppendFormat(\" `{0}` = @p{{0}},\", index++);", field.Name);
                                    builderOP.AppendLine("values.Add({1}.{0});", field.Name, target);
                                });
                            }

                            builderOP.AppendLine("if (index == 0) return;");
                            builderOP.AppendLine("builder[builder.Length - 1] = ' ';");
                            //builderOP.AppendLine("builder.Remove(builder.Length - 1, 1);");
                            // todo: Update 无法注入参数 _DB._T_BUS_DIS.Update(new T_BUS_DIS() { delete = true }, "WHERE county = @p0", ET_BUS_DIS.delete);
                            builderOP.AppendLine("if (!string.IsNullOrEmpty(condition)) builder.Append(condition);");
                            if (primaryFields.Count > 0)
                            {
                                // 应默认只更新实例的主键对应的实例
                                builderOP.AppendLine("else");
                                builderOP.AppendBlock(() =>
                                {
                                    builderOP.Append("builder.AppendFormat(\"WHERE ");
                                    for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                                    {
                                        var field = primaryFields[i];
                                        builderOP.Append("`{0}` = @p{{{1}}}", field.Name, i);
                                        if (i != n)
                                            builderOP.Append(" AND ");
                                    }
                                    builderOP.Append("\"");
                                    for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                                    {
                                        builderOP.Append(", index++");
                                        //if (i != n)
                                        //    builderOP.Append("++, ");
                                    }
                                    builderOP.AppendLine(");");
                                    for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                                    {
                                        builderOP.AppendLine("values.Add(target.{0});", primaryFields[i].Name);
                                    }
                                });
                            }
                            builderOP.AppendLine("builder.AppendLine(\";\");");
                            if (!string.IsNullOrEmpty(sharpIfBuild))
                                builderOP.AppendLine("#endif");
                            //builderOP.AppendLine("return builder.ToString();");
                        });

                        builderOP.AppendSummary("condition that 'where' or 'join' without ';'");
                        builderOP.AppendLine("public {1}int Update({0} target, string condition, params E{0}[] fields)", table.Name, _static);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("StringBuilder builder = new StringBuilder();");
                            builderOP.AppendLine("List<object> values = new List<object>(fields.Length + {0});", primaryFields.Count);
                            builderOP.AppendLine("GetUpdateSQL(target, condition, builder, values, fields);");
                            builderOP.AppendLine("return _DAO.ExecuteNonQuery(builder.ToString(), values.ToArray());");
                        });
                    }
                    // 内部级联更新级联关系
                    if (table.BaseType != null && typeof(InnerCascade).IsAssignableFrom(table))
                    {
                        builderOP.AppendLine("public static readonly E{0}[] CASCADE_PARENTS = new E{0}[] {{ E{0}.Parents }};", table.Name);
                        builderOP.AppendLine("public static readonly E{0}[] CASCADE_PARENTID = new E{0}[] {{ E{0}.ParentID, E{0}.Parents }};", table.Name);
                        builderOP.AppendLine("public int UpdateInnerCascade(int id, int newParentID)");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("var list = SelectMultiple(new E{0}[] {{ E{0}.ID, E{0}.ParentID }}, null);", table.Name);
                            builderOP.AppendLine("InnerCascade.UpdateCascadeParentID(list, id, newParentID);");
                            builderOP.AppendLine("StringBuilder builder = new StringBuilder();");
                            builderOP.AppendLine("List<object> param = new List<object>();");
                            builderOP.AppendLine("foreach (var item in list)");
                            builderOP.AppendBlock(() =>
                            {
                                builderOP.AppendLine("switch (item.ModifiedFlag)");
                                builderOP.AppendBlock(() =>
                                {
                                    builderOP.AppendLine("case InnerCascade.EModifiedFlag.Parents:");
                                    builderOP.AppendLine("GetUpdateSQL(item, null, builder, param, CASCADE_PARENTS); break;");
                                    builderOP.AppendLine("case InnerCascade.EModifiedFlag.ParentID:");
                                    builderOP.AppendLine("GetUpdateSQL(item, null, builder, param, CASCADE_PARENTID); break;");
                                    builderOP.AppendLine("default: continue;");
                                });
                            });
                            builderOP.AppendLine("return _DAO.ExecuteNonQuery(builder.ToString(), param.ToArray());");
                        });
                    }

                    // 查
                    builderOP.AppendLine("public {1}void GetSelectField(string tableName, StringBuilder builder, params E{0}[] fields)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        // 连接查询用
                        builderOP.AppendLine("if (string.IsNullOrEmpty(tableName)) tableName = \"`{0}`\";", table.Name);
                        builderOP.AppendLine("int count = fields == null ? 0 : fields.Length;");
                        builderOP.AppendLine("if (count == 0)");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("builder.Append(\"{0}.*\", tableName);");
                            builderOP.AppendLine("return;");
                        });
                        builderOP.AppendLine("count--;");
                        builderOP.AppendLine("for (int i = 0; i <= count; i++)");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("builder.Append(\"{0}.{1}\", tableName, fields[i].ToString());");
                            builderOP.AppendLine("if (i != count) builder.Append(\",\");");
                        });
                    });
                    builderOP.AppendLine("public {0}StringBuilder GetSelectSQL(params E{1}[] fields)", _static, table.Name);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("StringBuilder builder = new StringBuilder();");
                        builderOP.AppendLine("builder.AppendLine(\"SELECT \");");
                        builderOP.AppendLine("GetSelectField(null, builder, fields);");
                        builderOP.AppendLine("builder.AppendLine(\" FROM `{0}`\");", table.Name);
                        builderOP.AppendLine("return builder;");
                    });

                    // 主键查询
                    if (primaryFields.Count > 0)
                    {
                        builderOP.Append("public {0}{1} Select(", _static, table.Name);
                        foreach (var field in primaryFields)
                            builderOP.Append("{0} __{1}, ", field.FieldType.CodeName(), field.Name);
                        builderOP.AppendLine("params E{0}[] fields)", table.Name);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("StringBuilder builder = GetSelectSQL(fields);");
                            if (primaryFields.Count > 0)
                            {
                                builderOP.Append("builder.Append(\" WHERE ");
                                for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                                {
                                    var field = primaryFields[i];
                                    builderOP.Append("`{0}` = @p{1}", field.Name, i);
                                    if (i != n)
                                        builderOP.Append(" AND ");
                                }
                                builderOP.AppendLine(";\");");
                            }
                            builderOP.Append("var ret = ");
                            if (hasSpecial)
                                builderOP.Append("__TO(");
                            builderOP.Append("_DAO.SelectObject<{0}>(builder.ToString()", tableMapperName);
                            foreach (var field in primaryFields)
                                builderOP.Append(", __{0}", field.Name);
                            builderOP.Append(")");
                            if (hasSpecial)
                                builderOP.Append(")");
                            builderOP.AppendLine(";");
                            builderOP.AppendLine("if (ret != default({0}))", table.Name);
                            builderOP.AppendBlock(() =>
                            {
                                foreach (var field in primaryFields)
                                    builderOP.AppendLine("ret.{0} = __{0};", field.Name);
                            });
                            builderOP.AppendLine("return ret;");
                        });
                    }
                    // 条件查询
                    builderOP.AppendLine("public {0}{1} Select(E{1}[] fields, string condition, params object[] param)", _static, table.Name);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("StringBuilder builder = GetSelectSQL(fields);");
                        builderOP.AppendLine("if (!string.IsNullOrEmpty(condition)) builder.Append(\" {0}\", condition);");
                        builderOP.AppendLine("builder.Append(\';\');");
                        //builderOP.AppendLine("if (count == 0) return _DAO.SelectObject<{1}>(string.Format(\"SELECT * FROM {0} {{0}};\", condition));", table.Name, tableMapperName);
                        //builderOP.AppendLine("StringBuilder builder = new StringBuilder();");
                        //builderOP.AppendLine("builder.Append(\"SELECT\");");
                        //builderOP.AppendLine("count--;");
                        //builderOP.AppendLine("for (int i = 0; i <= count; i++)");
                        //builderOP.AppendBlock(() =>
                        //{
                        //    builderOP.AppendLine("builder.Append(\" `{0}`\", fields[i].ToString());");
                        //    builderOP.AppendLine("if (i != count) builder.Append(\",\");");
                        //});
                        //builderOP.AppendLine("builder.AppendLine(\" FROM {0} {{0}};\", condition);", table.Name);
                        builderOP.Append("return ");
                        if (hasSpecial)
                            builderOP.Append("__TO(");
                        builderOP.Append("_DAO.SelectObject<{0}>(builder.ToString(), param)", tableMapperName);
                        if (hasSpecial)
                            builderOP.Append(")");
                        builderOP.AppendLine(";");
                    });
                    // Exists
                    builderOP.Append("public {0}bool Exists(", _static);
                    for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                    {
                        var field = primaryFields[i];
                        builderOP.Append("{0} __{1}", field.FieldType.CodeName(), field.Name);
                        if (i != n)
                            builderOP.Append(", ");
                    }
                    builderOP.AppendLine(")");
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.Append("return _DAO.ExecuteScalar<bool>(\"SELECT EXISTS(SELECT 1 FROM `{0}`", table.Name);
                        if (primaryFields.Count > 0)
                        {
                            builderOP.Append(" WHERE ");
                            for (int i = 0, n = primaryFields.Count - 1; i <= n; i++)
                            {
                                var field = primaryFields[i];
                                builderOP.Append("`{0}` = @p{1}", field.Name, i);
                                if (i != n)
                                    builderOP.Append(" AND ");
                            }
                        }
                        builderOP.Append(")\"");
                        if (primaryFields.Count > 0)
                        {
                            foreach (var field in primaryFields)
                                builderOP.Append(", __{0}", field.Name);
                        }
                        builderOP.AppendLine(");");
                    });
                    // Exists2
                    builderOP.AppendLine("public {0}bool Exists2(string condition, params object[] param)", _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("return _DAO.ExecuteScalar<bool>(string.Format(\"SELECT EXISTS(SELECT 1 FROM `{0}` {{0}})\", condition), param);", table.Name);
                    });

                    builderOP.AppendLine("public {1}List<{0}> SelectMultiple(E{0}[] fields, string condition, params object[] param)", table.Name, _static);
                    builderOP.AppendBlock(() =>
                    {
                        builderOP.AppendLine("StringBuilder builder;");
                        builderOP.AppendLine("if (fields == null || fields.Length == 0) builder = new StringBuilder(\"SELECT * FROM {0}\");", table.Name);
                        builderOP.AppendLine("else builder = GetSelectSQL(fields);");
                        builderOP.AppendLine("if (!string.IsNullOrEmpty(condition)) builder.Append(\" {0}\", condition);");
                        builderOP.AppendLine("builder.Append(';');");
                        if (table.Name == tableMapperName)
                            builderOP.AppendLine("return _DAO.SelectObjects<{0}>(builder.ToString(), param);", tableMapperName);
                        else
                        {
                            builderOP.AppendLine("List<{0}> __temp = _DAO.SelectObjects<{0}>(builder.ToString(), param);", tableMapperName);
                            builderOP.AppendLine("List<{0}> __result = new List<{0}>(__temp.Count);", table.Name);
                            builderOP.AppendLine("__result.AddRange(__temp.Select(i => __TO(i)));");
                            builderOP.AppendLine("return __result;");
                        }
                    });
                    if (groupFields.Count > 0 || primaryFields.Count > 1)
                    {
                        IEnumerable<FieldInfo> group = null;
                        if (groupFields.Count > 0 && primaryFields.Count > 1)
                            group = groupFields.Concat(primaryFields);
                        else if (groupFields.Count > 0)
                            group = groupFields;
                        else
                            group = primaryFields;
                        // 所有Group键的组合条件查询
                        List<FieldInfo> __group = group.ToList();
                        IEnumerable<IEnumerable<FieldInfo>> groups = __group.Combination(1);
                        for (int i = 1; i < __group.Count; i++)
                            groups = groups.Concat(__group.Combination(i + 1));

                        foreach (var g in groups)
                        {
                            builderOP.Append("public {1}List<{0}> SelectMultipleBy", table.Name, _static);
                            var list = g.ToList();
                            for (int i = 0, e = list.Count - 1; i <= e; i++)
                            {
                                builderOP.Append(list[i].Name);
                                if (i != e)
                                    builderOP.Append('_');
                            }
                            builderOP.Append("(E{0}[] fields, ", table.Name);
                            for (int i = 0, e = list.Count - 1; i <= e; i++)
                                builderOP.Append("{0} {1}, ", list[i].FieldType.CodeName(), list[i].Name);
                            builderOP.Append("string conditionAfterWhere, params object[] param)");
                            builderOP.AppendLine();
                            builderOP.AppendBlock(() =>
                            {
                                builderOP.AppendLine("StringBuilder builder = GetSelectSQL(fields);");
                                builderOP.Append("builder.AppendFormat(\" WHERE ");
                                for (int i = 0, e = list.Count - 1; i <= e; i++)
                                {
                                    builderOP.Append("`{0}` = @p{{{1}}}", list[i].Name, i);
                                    if (i != e)
                                        builderOP.Append(" AND ");
                                }
                                builderOP.Append("\"");
                                for (int i = 0, e = list.Count - 1; i <= e; i++)
                                    builderOP.Append(", param.Length + {0}", i);
                                builderOP.AppendLine(");");
                                builderOP.AppendLine("if (!string.IsNullOrEmpty(conditionAfterWhere)) builder.Append(\" {0}\", conditionAfterWhere);");
                                builderOP.AppendLine("builder.Append(';');");
                                if (table.Name == tableMapperName)
                                {
                                    builderOP.Append("if (param.Length == 0) return _DAO.SelectObjects<{0}>(builder.ToString()", tableMapperName);
                                    for (int i = 0, e = list.Count - 1; i <= e; i++)
                                        builderOP.Append(", {0}", list[i].Name);
                                    builderOP.AppendLine(");");
                                    builderOP.Append("else return _DAO.SelectObjects<{0}>(builder.ToString(), param.Add(", tableMapperName);
                                    for (int i = 0, e = list.Count - 1; i <= e; i++)
                                    {
                                        builderOP.Append(list[i].Name);
                                        if (i != e)
                                            builderOP.Append(", ");
                                    }
                                    builderOP.AppendLine("));");
                                }
                                else
                                {
                                    builderOP.AppendLine("List<{0}> __temp;", tableMapperName);
                                    builderOP.Append("if (param.Length == 0) __temp = _DAO.SelectObjects<{0}>(builder.ToString()", tableMapperName);
                                    for (int i = 0, e = list.Count - 1; i <= e; i++)
                                        builderOP.Append(", {0}", list[i].Name);
                                    builderOP.AppendLine(");");
                                    builderOP.Append("else __temp = _DAO.SelectObjects<{0}>(builder.ToString(), param.Add(", tableMapperName);
                                    for (int i = 0, e = list.Count - 1; i <= e; i++)
                                    {
                                        builderOP.Append(list[i].Name);
                                        if (i != e)
                                            builderOP.Append(", ");
                                    }
                                    builderOP.AppendLine("));");
                                    //builderOP.AppendLine("return new List<{0}>(__temp.ToArray());", table.Name);
                                    builderOP.AppendLine("List<{0}> __result = new List<{0}>(__temp.Count);", table.Name);
                                    builderOP.AppendLine("__result.AddRange(__temp.Select(i => __TO(i)));");
                                    builderOP.AppendLine("return __result;");
                                }
                            });
                        }
                    }

                    // 外键列
                    var foreignFields = fields.Where(f => f.HasAttribute<ForeignAttribute>()).ToArray();
                    if (foreignFields.Length > 0)
                    {
                        // 外键列引用的表
                        Type[] refferenceTypes = new Type[foreignFields.Length];
                        for (int i = 0; i < refferenceTypes.Length; i++)
                            refferenceTypes[i] = tree.Find(f => f.Field == foreignFields[i]).Parent.Table;

                        int foreignCount = foreignFields.Length;
                        int e = foreignCount - 1;

                        // 构造查询的字段
                        builderOP.Append("public {0}StringBuilder GetSelectJoinSQL(", _static);
                        builderOP.Append("ref E{0}[] f{0}", table.Name);
                        for (int i = 0; i <= e; i++)
                            builderOP.Append(", ref E{0}[] f{1}", refferenceTypes[i].Name, foreignFields[i].Name);
                        builderOP.AppendLine(")");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("StringBuilder builder = new StringBuilder();");
                            builderOP.AppendLine("builder.Append(\"SELECT \");");
                            builderOP.AppendLine("{1}_{0}.GetSelectField(\"t0\", builder, f{0});", table.Name, dbInstanceName);
                            builderOP.AppendLine("if (f{0} == null || f{0}.Length == 0) f{0} = FIELD_ALL;", table.Name);
                            //builderOP.AppendLine("List<{0}> t{0} = new List<{0}>();", table.Name);
                            for (int i = 0; i <= e; i++)
                            {
                                //builderOP.AppendLine("List<{0}> t{1};", foreigns[i].ForeignTable.Name, foreignFields[i].Name);
                                builderOP.AppendLine("if (f{0} != null)", foreignFields[i].Name);
                                builderOP.AppendBlock(() =>
                                {
                                    builderOP.AppendLine("builder.Append(\", \");");
                                    builderOP.AppendLine("{3}_{0}.GetSelectField(\"t{2}\", builder, f{1});", refferenceTypes[i].Name, foreignFields[i].Name, (i + 1), dbInstanceName);
                                    //builderOP.AppendLine("t{0} = new List<{1}>();", foreignFields[i].Name, foreigns[i].ForeignTable.Name);
                                    builderOP.AppendLine("if (f{0}.Length == 0) f{0} = {2}_{1}.FIELD_ALL;", foreignFields[i].Name, refferenceTypes[i].Name, dbInstanceName);
                                });
                                //builderOP.AppendLine("else t{0} = null;", foreignFields[i].Name);
                            }
                            builderOP.AppendLine("builder.Append(\" FROM `{0}` as t0\");", table.Name);
                            for (int i = 0; i <= e; i++)
                            {
                                builderOP.AppendLine("if (f{1} != null) builder.Append(\" LEFT JOIN `{0}` as t{2} ON (t0.{1} = t{2}.{3})\");", refferenceTypes[i].Name, foreignFields[i].Name, (i + 1), foreignFields[i].GetAttribute<ForeignAttribute>().ForeignField);
                            }
                            builderOP.AppendLine("return builder;");
                        });

                        // 读取Join对象
                        builderOP.Append("public {0}void SelectJoinRead(IDataReader reader, List<Join{1}> list, ", _static, table.Name);
                        builderOP.Append("E{0}[] f{0}", table.Name);
                        for (int i = 0; i <= e; i++)
                            builderOP.Append(", E{0}[] f{1}", refferenceTypes[i].Name, foreignFields[i].Name);
                        builderOP.AppendLine(")");
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("int offset = 0;");
                            builderOP.AppendLine("int[] indices = new int[reader.FieldCount];");
                            builderOP.AppendLine("List<PropertyInfo> _p{0};", table.Name);
                            builderOP.AppendLine("List<FieldInfo> _f{0};", table.Name);
                            builderOP.AppendLine("{1}_{0}.MultiReadPrepare(reader, 0, f{0}.Length, out _p{0}, out _f{0}, ref indices);", table.Name, dbInstanceName);
                            builderOP.AppendLine("offset = f{0}.Length;", table.Name);
                            for (int i = 0; i <= e; i++)
                            {
                                builderOP.AppendLine("List<PropertyInfo> _p{0} = null;", foreignFields[i].Name);
                                builderOP.AppendLine("List<FieldInfo> _f{0} = null;", foreignFields[i].Name);
                                builderOP.AppendLine("if (f{0} != null)", foreignFields[i].Name);
                                builderOP.AppendBlock(() =>
                                {
                                    builderOP.AppendLine("{2}_{1}.MultiReadPrepare(reader, offset, f{0}.Length, out _p{0}, out _f{0}, ref indices);", foreignFields[i].Name, refferenceTypes[i].Name, dbInstanceName);
                                    builderOP.AppendLine("offset += f{0}.Length;", foreignFields[i].Name);
                                });
                            }
                            builderOP.AppendLine("while (reader.Read())");
                            builderOP.AppendBlock(() =>
                            {
                                builderOP.AppendLine("Join{0} join = new Join{0}();", table.Name);
                                builderOP.AppendLine("list.Add(join);");
                                //builderOP.AppendLine("t{0}.Add(_{0}.Read(reader, 0, f{0}.Length));", table.Name);
                                builderOP.AppendLine("join.{0} = {1}_{0}.MultiRead(reader, 0, f{0}.Length, _p{0}, _f{0}, indices);", table.Name, dbInstanceName);
                                builderOP.AppendLine("offset = f{0}.Length;", table.Name);
                                for (int i = 0; i <= e; i++)
                                {
                                    builderOP.AppendLine("if (f{0} != null)", foreignFields[i].Name);
                                    builderOP.AppendBlock(() =>
                                    {
                                        //builderOP.AppendLine("t{0}.Add(_{1}.Read(reader, offset, f{0}.Length));", foreignFields[i].Name, foreigns[i].ForeignTable.Name);
                                        builderOP.AppendLine("join.{0} = {2}_{1}.MultiRead(reader, offset, f{0}.Length, _p{0}, _f{0}, indices);", foreignFields[i].Name, refferenceTypes[i].Name, dbInstanceName);
                                        builderOP.AppendLine("offset += f{0}.Length;", foreignFields[i].Name);
                                    });
                                }
                            });
                        });

                        // 外键连接查询
                        builderOP.Append("public {0}List<Join{1}> SelectJoin(", _static, table.Name);
                        builderOP.Append("E{0}[] f{0}", table.Name);
                        for (int i = 0; i <= e; i++)
                            builderOP.Append(", E{0}[] f{1}", refferenceTypes[i].Name, foreignFields[i].Name);
                        builderOP.AppendLine(", string condition, params object[] param)");
                        builderOP.AppendBlock(() =>
                        {
                            // 查询读取数据
                            builderOP.Append("StringBuilder builder = GetSelectJoinSQL(");
                            builderOP.Append("ref f{0}", table.Name);
                            for (int i = 0; i <= e; i++)
                                builderOP.Append(", ref f{0}", foreignFields[i].Name);
                            builderOP.AppendLine(");");
                            builderOP.AppendLine("if (!string.IsNullOrEmpty(condition)) builder.Append(\" {0}\", condition);");
                            builderOP.AppendLine("builder.Append(';');");

                            builderOP.AppendLine("List<Join{0}> results = new List<Join{0}>();", table.Name);
                            builderOP.Append("_DAO.ExecuteReader((reader) => SelectJoinRead(reader, results");
                            builderOP.Append(", f{0}", table.Name);
                            for (int i = 0; i <= e; i++)
                                builderOP.Append(", f{0}", foreignFields[i].Name);
                            builderOP.Append(")");
                            builderOP.AppendLine(", builder.ToString(), param);");

                            builderOP.AppendLine("return results;");
                        });

                        // 外键连接翻页查询
                        if (fields.Length > 0)
                        {
                            builderOP.Append("public {0}PagedModel<Join{1}> SelectJoinPages(", _static, table.Name);
                            builderOP.Append("E{0}[] f{0}", table.Name);
                            for (int i = 0; i <= e; i++)
                                builderOP.Append(", E{0}[] f{1}", refferenceTypes[i].Name, foreignFields[i].Name);
                            builderOP.AppendLine(", string __where, string conditionAfterWhere, int page, int pageSize, params object[] param)");
                            builderOP.AppendBlock(() =>
                            {
                                builderOP.Append("StringBuilder builder = GetSelectJoinSQL(");
                                builderOP.Append("ref f{0}", table.Name);
                                for (int i = 0; i <= e; i++)
                                    builderOP.Append(", ref f{0}", foreignFields[i].Name);
                                builderOP.AppendLine(");");

                                builderOP.AppendLine("StringBuilder builder2 = new StringBuilder();");
                                builderOP.AppendLine("builder2.Append(\"SELECT count(t0.`{1}`) FROM `{0}` as t0\");", table.Name, fields[0].Name);
                                for (int i = 0; i <= e; i++)
                                {
                                    builderOP.AppendLine("if (f{1} != null) builder2.Append(\" LEFT JOIN `{0}` as t{2} ON (t0.{1} = t{2}.{3})\");", refferenceTypes[i].Name, foreignFields[i].Name, (i + 1), foreignFields[i].GetAttribute<ForeignAttribute>().ForeignField);
                                }

                                builderOP.Append("return {2}.SelectPages<Join{0}>(_DAO, builder2.ToString(), __where, builder.ToString(), conditionAfterWhere, page, pageSize, (reader, list) => SelectJoinRead(reader, list, ", table.Name, fields[0].Name, nsOrEmptyDotDBnameOrEmpty);
                                builderOP.Append("f{0}", table.Name);
                                for (int i = 0; i <= e; i++)
                                    builderOP.Append(", f{0}", foreignFields[i].Name);
                                builderOP.AppendLine("), param);");
                            });
                        }
                    }

                    // 翻页查询
                    if (hasSpecial)
                    {
                        builderOP.AppendLine("public {0}PagedModel<{1}> ChangePageModel(PagedModel<{2}> model)", _static, table.Name, tableMapperName);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("return model.ChangeModel<{0}>(m => __TO(m));", table.Name);
                        });
                    }
                    if (fields.Length > 0)
                    {
                        builderOP.AppendLine("public {0}PagedModel<{1}> SelectPages(string __where, E{1}[] fields, string conditionAfterWhere, int page, int pageSize, params object[] param)", _static, table.Name);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("var ret = SelectPages<{0}>(__where, GetSelectSQL(fields).ToString(), conditionAfterWhere, page, pageSize, param);", tableMapperName);
                            if (hasSpecial)
                                builderOP.AppendLine("return ChangePageModel(ret);");
                            else
                                builderOP.AppendLine("return ret;");
                        });
                        builderOP.AppendLine("public {0}PagedModel<T> SelectPages<T>(string __where, string selectSQL, string conditionAfterWhere, int page, int pageSize, params object[] param) where T : new()", _static);
                        builderOP.AppendBlock(() =>
                        {
                            builderOP.AppendLine("return {1}.SelectPages<T>(_DAO, \"SELECT count(*) FROM `{0}`\", __where, selectSQL, conditionAfterWhere, page, pageSize, param);", table.Name, nsOrEmptyDotDBnameOrEmpty);
                        });
                    }

                    // ___类型结束
                    if (!isStatic)
                        builderOP.AppendLine("}");

                    #endregion

                    if (hasSpecial && !isStatic)
                        // 拥有特殊类型字段的数据库映射类型结束
                        builder.AppendLine("}");
                    // 数据库操作方法
                    builder.Append(builderOP);
                    if (isStatic)
                        // 静态时操作方法肯定在映射类中
                        builder.AppendLine("}");
                }
                #endregion

                #region 针对对应数据库的分页查询等通用操作
                builder.AppendLine("public static PagedModel<T> SelectPages<T>(_DATABASE.Database db, string selectCountSQL, string __where, string selectSQL, string conditionAfterWhere, int page, int pageSize, params object[] param) where T : new()");
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("return SelectPages(db, selectCountSQL, __where, selectSQL, conditionAfterWhere, page, pageSize, new Action<IDataReader, List<T>>((reader, list) => { while (reader.Read()) list.Add(_DATABASE.ReadObject<T>(reader, 0, reader.FieldCount)); }), param);");
                });
                builder.AppendLine("public static PagedModel<T> SelectPages<T>(_DATABASE.Database db, string selectCountSQL, string __where, string selectSQL, string conditionAfterWhere, int page, int pageSize, Action<IDataReader, List<T>> read, params object[] param)");
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("StringBuilder builder = new StringBuilder();");
                    builder.AppendLine("PagedModel<T> result = new PagedModel<T>();");
                    builder.AppendLine("if (page < 0)");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("builder.AppendLine(\"{0} {1} {2}\", selectSQL, __where, conditionAfterWhere);");
                        builder.AppendLine("db.ExecuteReader((reader) =>");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("result.Models = new List<T>();");
                            builder.AppendLine("read(reader, result.Models);");
                            builder.AppendLine("result.Count = result.Models.Count;");
                        });
                        builder.AppendLine(", builder.ToString(), param);");
                        builder.AppendLine("return result;");
                    });
                    builder.AppendLine("builder.AppendLine(\"{0} {1};\", selectCountSQL, __where);");
                    builder.AppendLine("builder.AppendLine(\"{0} {1} {2} LIMIT @p{3},@p{4};\", selectSQL, __where, conditionAfterWhere, param.Length, param.Length + 1);");
                    builder.AppendLine("object[] __param = new object[param.Length + 2];");
                    builder.AppendLine("Array.Copy(param, __param, param.Length);");
                    builder.AppendLine("__param[param.Length] = page * pageSize;");
                    builder.AppendLine("__param[param.Length + 1] = pageSize;");
                    builder.AppendLine("result.Page = page;");
                    builder.AppendLine("result.PageSize = pageSize;");
                    builder.AppendLine("db.ExecuteReader((reader) =>");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("if (reader.Read()) result.Count = (int)(long)reader[0];");
                        builder.AppendLine("result.Models = new List<T>();");
                        builder.AppendLine("reader.NextResult();");
                        builder.AppendLine("read(reader, result.Models);");
                    });
                    builder.AppendLine(", builder.ToString(), __param);");
                    builder.AppendLine("result.CalcTotalPage();");
                    builder.AppendLine("return result;");
                });
                builder.AppendLine("public static void MasterSlave(string masterConnString, string slaveConnStrings)");
                builder.AppendBlock(() =>
                {
                    builder.AppendLine("Dictionary<string, string> dic = _DATABASE.ParseConnectionString(masterConnString, true);");
                    builder.AppendLine("string host = dic[\"server\"];");
                    builder.AppendLine("string port = dic[\"port\"];");
                    builder.AppendLine("string user = dic[\"user\"];");
                    builder.AppendLine("string password = dic[\"password\"];");
                    builder.AppendLine("MASTER_STATUS masterStatus;");
                    builder.AppendLine("using (MYSQL_DATABASE master = new MYSQL_DATABASE())");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("master.ConnectionString = masterConnString;");
                        builder.AppendLine("master.TestConnection();");
                        builder.AppendLine("masterStatus = master.SelectObject<MASTER_STATUS>(\"SHOW MASTER STATUS\");");
                    });
                    builder.AppendLine("string user2 = null;");
                    builder.AppendLine("string password2 = null;");
                    builder.AppendLine("StringBuilder builder = new StringBuilder();");
                    builder.AppendLine("builder.AppendLine(\"stream {\");");
                    builder.AppendLine("builder.AppendFormat(\"    upstream {0} {{\", masterStatus.Binlog_Do_DB);");
                    builder.AppendLine("builder.AppendLine();");
                    builder.AppendLine("string[] slaves = slaveConnStrings.Split(',');");
                    builder.AppendLine("for (int i = 0; i < slaves.Length; i++)");
                    builder.AppendBlock(() =>
                    {
                        builder.AppendLine("using (MYSQL_DATABASE slave = new MYSQL_DATABASE())");
                        builder.AppendBlock(() =>
                        {
                            builder.AppendLine("var dic2 = _DATABASE.ParseConnectionString(slaves[i], true);");
                            builder.AppendLine("if (user2 == null) user2 = dic2[\"user\"];");
                            builder.AppendLine("else if (user2 != dic2[\"user\"]) throw new InvalidOperationException(\"从库作为分布式读库时登录用户名必须一致\");");
                            builder.AppendLine("if (password2 == null) password2 = dic2[\"password\"];");
                            builder.AppendLine("else if (password2 != dic2[\"password\"]) throw new InvalidOperationException(\"从库作为分布式读库时登录密码必须一致\");");
                            builder.AppendLine("builder.AppendLine(\"        server {0}:{1};\", dic2[\"server\"], dic2[\"port\"]);");
                            builder.AppendLine("slave.ConnectionString = slaves[i];");
                            builder.AppendLine("slave.TestConnection();");
                            builder.AppendLine("var slaveStatus = slave.SelectObject<SLAVE_STATUS>(\"SHOW SLAVE STATUS\");");
                            builder.AppendLine("if (slaveStatus == null || slaveStatus.IsRunning) continue;");
                            builder.AppendLine("slave.ExecuteNonQuery(\"CHANGE MASTER TO MASTER_HOST=@p0,MASTER_PORT=@p1,MASTER_USER=@p2,MASTER_PASSWORD=@p3,MASTER_LOG_FILE=@p4,MASTER_LOG_POS=@p5;\", host, port, user, password, masterStatus.File, masterStatus.Position);");
                        });
                    });
                    builder.AppendLine("builder.AppendLine(\"    }\");");
                    builder.AppendLine("builder.AppendLine(\"    server {\");");
                    builder.AppendLine("builder.AppendLine(\"        listen nginxport;\");");
                    builder.AppendLine("builder.AppendLine(\"        proxy_pass {0};\", masterStatus.Binlog_Do_DB);");
                    builder.AppendLine("builder.AppendLine(\"    }\");");
                    builder.AppendLine("builder.AppendLine(\"}\");");
                    builder.AppendLine("_LOG.Debug(\"Nginx配置文件代码\\r\\n{0}\", builder.ToString());");
                    builder.AppendLine("_LOG.Debug(\"服务器启动的主从数据库连接字符串配置命令\");");
                    builder.AppendLine("_LOG.Info(\"\\\"Server={0};Port={1};User={2};Password={3}; {4} Server=nginxip;Port=nginxport;User={5};Password={6};\\\"\", host, port, user, password, masterStatus.Binlog_Do_DB, user2, password2);");
                });

                #endregion
            });
            builder.AppendLine("}");

            SaveCode(outputCS, builder);
            Console.WriteLine("Build mysql completed.");
        }
        public static void BuildTypeScriptTypeFromDll(string dll, string outputTS)
        {
            Assembly assembly = Assembly.LoadFrom(Path.GetFullPath(dll));
            var types = assembly.GetTypes();
            StringBuilder builder = new StringBuilder();
            foreach (var item in types)
            {
                if (item.IsStatic())
                    continue;
                if (item.IsClass)
                {
                    if (item.IsSpecialName || !item.IsVisible) continue;
                    builder.Append("export class {0}", item.Name);
                    if (item.BaseType != null && item.BaseType != typeof(object))
                        builder.Append(" extends " + item.BaseType.Name);
                    builder.AppendLine();
                    builder.AppendBlock(() =>
                    {
                        var fields = item.GetFields(BindingFlags.Instance | BindingFlags.Public);
                        foreach (var field in fields)
                            if (field.DeclaringType == item)
                                builder.AppendLine("{0}: {1}", field.Name, GetTSType(field.FieldType));
                    });
                }
                else if (item.IsEnum)
                {
                    builder.AppendLine("export enum {0}", item.Name);
                    var instance = Activator.CreateInstance(item);
                    builder.AppendBlock(() =>
                    {
                        var fields = item.GetFields(BindingFlags.Public | BindingFlags.Static);
                        foreach (var field in fields)
                        {
                            object value = field.GetValue(instance);
                            builder.AppendLine("{0} = {1},", value, Convert.ChangeType(value, Enum.GetUnderlyingType(item)));
                        }
                    });
                }
            }
            SaveCode(Path.ChangeExtension(outputTS, "ts"), builder.ToString());
            Console.WriteLine("生成完毕！共生成{0}个类型", types.Length);
        }
        public static void DeleteContentExcept(string dir, string exceptSplitByComma)
        {
            HashSet<string> split = new HashSet<string>(exceptSplitByComma.Split(';'));
            foreach (var item in Directory.GetDirectories(dir))
            {
                string target = Path.GetFileName(item);
                if (!split.Contains(target))
                {
                    Directory.Delete(item, true);
                    _LOG.Debug("删除文件夹：{0}", target);
                }
                else
                    _LOG.Debug("忽略文件夹：{0}", target);
            }
            foreach (var item in Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly))
            {
                File.Delete(item);
                _LOG.Debug("删除文件：{0}", item);
            }
        }
    }
}
